10

Possible Duplicate:
What’s the point in defaulting functions in C++11?

C++11 introduced defaulted methods (e.g. void myMethod() = default;).

What does it do to methods (how do methods behave after being defaulted). How do I use them properly (what are its uses)?

Community
  • 1
  • 1
Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
  • 2
    Stroustrup included this in his [C++11 FAQ](http://stroustrup.com/C++11FAQ.html#default). – chris Dec 01 '12 at 00:42
  • @chris Yes. I know. I just wanted to hear from the community of what they think about it. – Mark Garcia Dec 01 '12 at 00:43
  • This is a rather broad question for SO in my opinion... – ildjarn Dec 01 '12 at 00:45
  • @ildjarn I think it's specific enough. – Mark Garcia Dec 01 '12 at 00:45
  • It's useful if you want a compiler generated implementation of one of the defaultable(?) functions, but with a difference access level. – goji Dec 01 '12 at 00:47
  • 3
    Check here - http://stackoverflow.com/questions/823935/whats-the-point-in-defaulting-functions-in-c11 . Also I don't think you can make a method like myMethod as `default`. This is for stuff like ctors, copy ctors etc. – user93353 Dec 01 '12 at 00:58

3 Answers3

4

There are a number of class members that are considered "special member functions" by the C++ standard. These are:

  • The default constructor (a constructor that can be called with no parameters).
  • The copy constructor (a constructor that can be called with one parameter that is the object type as an lvalue reference).
  • The copy assignment operator (an operator= overload that can be called with one parameter that is the object type as either an lvalue reference or a value).
  • The move constructor (a constructor that can be called with one parameter that is the object type as an rvalue reference).
  • The move assignment operator (an operator= overload that can be called with one parameter that is the object type as either an rvalue reference or a value).
  • The destructor.

These member functions are special in that the language does special things with them on types. Another thing that makes them special is that the compiler can provide definitions for them if you do not. These are the only functions that you can use the = default; syntax on.

The issue is that the compiler will only provide a definition under certain conditions. That is, there are conditions under which a definition will not be provided.

I won't go over the entire list, but one example is what others have mentioned. If you provide a constructor for a type that is not a special constructor (ie: not one of the constructors mentioned above), a default constructor will not be automatically generated. Therefore, this type:

struct NoDefault
{
  NoDefault(float f);
};

NoDefault cannot be default constructed. Therefore, it cannot be used in any context where default construction is needed. You can't do NoDefault() to create a temporary. You can't create arrays of NoDefault (since those are default constructed). You cannot create a std::vector<NoDefault> and call the sizing constructor without providing a value to copy from, or any other operation that requires that the type be DefaultConstructible.

However, you could do this:

struct UserDefault
{
  UserDefault() {}
  UserDefault(float f);
};

That would fix everything, right?

WRONG!

That is not the same thing as this:

struct StdDefault
{
  StdDefault() = default;
  StdDefault(float f);
};

Why? Because StdDefault is a trivial type. What does that mean? I won't explain the whole thing, but go here for the details. Making types trivial is often a useful feature to have, when you can do it.

One of the requirements of a trivial type is that it does not have a user-provided default constructor. UserDefault has a provided one, even though it does the exact same thing as the compiler-generated one would. Therefore, UserDefault is not trivial. StdDefault is a trivial type because = default syntax means that the compiler will generate it. So it's not user-provided.

The = default syntax tells the compiler, "generate this function anyway, even if you normally wouldn't." This is important to ensure that a class is a trivial type, since you cannot actually implement special members the way the compiler can. It allows you to force the compiler to generate the function even when it wouldn't.

This is very useful in many circumstances. For example:

class UniqueThing
{
public:
  UniqueThing() : m_ptr(new SomeType()) {}
  UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
  UniqueThing &operator =(const UniqueThing &ptr)
  {
    m_ptr.reset(new SomeType(*ptr)); return *this;
  }

private:
  std::unique_ptr<SomeType> m_ptr;
};

We must write every one of these functions to make the class copy the contents of the unique_ptr; there's no way to avoid that. But we also want this to be moveable, and the move constructor/assignment operators won't automatically be generated for us. It'd silly to re-implement them (and error-prone if we add more members), so we can use the = default syntax:

class UniqueThing
{
public:
  UniqueThing() : m_ptr(new SomeType()) {}
  UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
  UniqueThing(UniqueThing &&ptr) = default;
  UniqueThing &operator =(const UniqueThing &ptr)
  {
    m_ptr.reset(new SomeType(*ptr)); return *this;
  }

  UniqueThing &operator =(UniqueThing &&ptr) = default;

private:
  std::unique_ptr<SomeType> m_ptr;
};
Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
3

Actually, default can only apply to special methods - i.e. constructors, destructors, assignment operator:

8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]

1 A function definition of the form: attribute-specifier-seqopt decl-specifier-seqopt declarator = default ; is called an explicitly-defaulted definition. A function that is explicitly defaulted shall
— be a special member function,
— have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared, and
— not have default arguments.

They behave the same as if the compiler generated them and are useful for cases like:

class Foo{
     Foo(int) {}
};

here, the default constructor doesn't exist, so Foo f; wouldn't be valid. However, you can tell the compiler that you want a default constructor without the need to implement it yourself:

class Foo{
     Foo() = default;
     Foo(int) {}
};

EDIT: Pointed out by @Nicol Bolas in the comments, this truly doesn't explain why you'd prefer this over Foo() {}. What I'm guessing is: if you have a class w/ implementations separated in an implementation file, your class definition (if you don't use =default) would look like:

class Foo{
     Foo();
     Foo(int);
};

What I'm assuming is that the =default is meant to provide an idiomatic way of telling you "we're not doing anything special in the constructor". With the above class definition, the default constructor could very well not value-initialize the class members. With the =default you have that guarantee just by looking at the header.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
3

Using = default on a constructor or copy-constructor means that the compiler will generate that function for you during compile-time. This happens normally when you exclude the respective functions from your class/struct. And in the case where you define your own constructor, this new syntax allows you to explicitly tell the compiler to default-construct the constructor where it normally won't. Take for instance:

struct S {

};

int main() {

    S s1; // 1
    S s2(s1); // 2

}

The compiler will generate both a copy-constructor and a default-constructor, thus allowing us to instantiate an S object like we did (1) and copy s2 into s1 (2). But if we define our own constructor, we find that we are unable to instantiate in the same way:

struct S {
    S(int);
};

int main() {

    S s;    // illegal
    S s(5); // legal

}

This fails because S has been given a custom-defined constructor, and the compiler won't generate the default one. But if we still want the compiler to give us one, we can explicitly convey this using default:

struct S {
    S(int);
    S() = default;
    S(const S &) = default;
};

int main() {

    S s;     // legal
    S s(5);  // legal

    S s2(s); // legal

}

Note, however, that only certain functions with certain function-signatures can be overloaded. It follows that this will fail because it is neither a copy-constructor nor a default-constructor or a move or assignment operator:

struct S {
    S(int) = default; // error
};

We are also able to explicitly define the assignment operator (just like the default/copy-constructor, a default one is generated for us when we do not write out our own). So take for example:

struct S {
    int operator=(int) {
        // do our own stuff
    }
};

int main() {

    S s;

    s = 4;

}

This compiles, but it won't work for us in the general case where we want to assign another S object into s. This is because since we wrote our own, the compiler doesn't provide one for us. This is where default comes into play:

struct S {
    int operator=(int) {
        // do our own stuff
    }

    S & operator=(const S &) = default;
};

int main() {

    S s1, s2;

    s1 = s2; // legal

}

But technically it's superfluous to define the assignment operator, copy, or move constructor as default when there is no other function "overriding" it as the compiler will provide one for you in that case anyway. The only case in which it won't provide you one is where you define your own one (or variation thereof). But as a matter of preference, asking the compiler to provide a default function using the new aforementioned syntax is more explicit and easier to see and understand if intentionally left out for the compiler.

template boy
  • 10,230
  • 8
  • 61
  • 97