What's the Point in Defaulting Functions in C++11

What's the point in defaulting functions in C++11?

A defaulted constructor will have a declaration, and that declaration will be subject to the normal access rules. E.g. you can make the default copy constructor protected. Without these new declarations, the default generated members are public.

What does default mean after a class' function declaration?

It's a new C++11 feature.

It means that you want to use the compiler-generated version of that function, so you don't need to specify a body.

You can also use = delete to specify that you don't want the compiler to generate that function automatically.

With the introduction of move constructors and move assignment operators, the rules for when automatic versions of constructors, destructors and assignment operators are generated has become quite complex. Using = default and = delete makes things easier as you don't need to remember the rules: you just say what you want to happen.

The new syntax = default in C++11

A defaulted default constructor is specifically defined as being the same as a user-defined default constructor with no initialization list and an empty compound statement.

§12.1/6 [class.ctor] A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used to create an object of its class type or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement. [...]

However, while both constructors will behave the same, providing an empty implementation does affect some properties of the class. Giving a user-defined constructor, even though it does nothing, makes the type not an aggregate and also not trivial. If you want your class to be an aggregate or a trivial type (or by transitivity, a POD type), then you need to use = default.

§8.5.1/1 [dcl.init.aggr] An aggregate is an array or a class with no user-provided constructors, [and...]

§12.1/5 [class.ctor] A default constructor is trivial if it is not user-provided and [...]

§9/6 [class] A trivial class is a class that has a trivial default constructor and [...]

To demonstrate:

#include <type_traits>

struct X {
X() = default;
};

struct Y {
Y() { };
};

int main() {
static_assert(std::is_trivial<X>::value, "X should be trivial");
static_assert(std::is_pod<X>::value, "X should be POD");

static_assert(!std::is_trivial<Y>::value, "Y should not be trivial");
static_assert(!std::is_pod<Y>::value, "Y should not be POD");
}

Additionally, explicitly defaulting a constructor will make it constexpr if the implicit constructor would have been and will also give it the same exception specification that the implicit constructor would have had. In the case you've given, the implicit constructor would not have been constexpr (because it would leave a data member uninitialized) and it would also have an empty exception specification, so there is no difference. But yes, in the general case you could manually specify constexpr and the exception specification to match the implicit constructor.

Using = default does bring some uniformity, because it can also be used with copy/move constructors and destructors. An empty copy constructor, for example, will not do the same as a defaulted copy constructor (which will perform member-wise copy of its members). Using the = default (or = delete) syntax uniformly for each of these special member functions makes your code easier to read by explicitly stating your intent.

Why should default parameters be added last in C++ functions?

To simplify the language definition and keep code readable.

void foo(int x = 2, int y);

To call that and take advantage of the default value, you'd need syntax like this:

foo(, 3);

Which was probably felt to be too weird. Another alternative is specifying names in the argument list:

foo(y : 3);

A new symbol would have to be used because this already means something:

foo(y = 3); // assign 3 to y and then pass y to foo.

The naming approach was considered and rejected by the ISO committee because they were uncomfortable with introducing a new significance to parameter names outside of the function definition.

If you're interested in more C++ design rationales, read The Design and Evolution of C++ by Stroustrup.

C++11: When do we need to specialize =default for default member function?

because if I don't give definition of special member functions in "Derived", compiler will generate one for me, equal to using "=default".

Because there're cases that some of the special member functions won't be generated, e.g. when you declare copy constructor, move constructor won't be generated, then move requests would be processed by copy constructor. Adding (defaulted) move constructor could prevent that:

struct Derived {
Derived(const Derived&) { ... }
Derived(Derived&&) = default;
};

How is =default different from {} for default constructor and destructor?

This is a completely different question when asking about constructors than destructors.

If your destructor is virtual, then the difference is negligible, as Howard pointed out. However, if your destructor was non-virtual, it's a completely different story. The same is true of constructors.

Using = default syntax for special member functions (default constructor, copy/move constructors/assignment, destructors etc) means something very different from simply doing {}. With the latter, the function becomes "user-provided". And that changes everything.

This is a trivial class by C++11's definition:

struct Trivial
{
int foo;
};

If you attempt to default construct one, the compiler will generate a default constructor automatically. Same goes for copy/movement and destructing. Because the user did not provide any of these member functions, the C++11 specification considers this a "trivial" class. It therefore legal to do this, like memcpy their contents around to initialize them and so forth.

This:

struct NotTrivial
{
int foo;

NotTrivial() {}
};

As the name suggests, this is no longer trivial. It has a default constructor that is user-provided. It doesn't matter if it's empty; as far as the rules of C++11 are concerned, this cannot be a trivial type.

This:

struct Trivial2
{
int foo;

Trivial2() = default;
};

Again as the name suggests, this is a trivial type. Why? Because you told the compiler to automatically generate the default constructor. The constructor is therefore not "user-provided." And therefore, the type counts as trivial, since it doesn't have a user-provided default constructor.

The = default syntax is mainly there for doing things like copy constructors/assignment, when you add member functions that prevent the creation of such functions. But it also triggers special behavior from the compiler, so it's useful in default constructors/destructors too.

Point of instantiation of default arguments in a template function

As quoted in the question [temp.point]/8 says:

If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

According to the one-definition rule two definitions are not the same if function call overload resolution of a name used in the definition will yield different entities defined outside the definition. ([basic.def.odr]/6.2)

Overload resolution of the two calls to show in func<A> and in Fun<A> will choose different function overloads depending on whether func<A>'s point of instantiation is immediately after main or at the end of the translation unit, both of which are allowed points of instantiation.

Therefore the program is ill-formed, no diagnostic required.

Default value of function parameter

If you put the declaration in a header file, and the definition in a separate .cpp file, and #include the header from a different .cpp file, you will be able to see the difference.

Specifically, suppose:

lib.h

int Add(int a, int b);

lib.cpp

int Add(int a, int b = 3) {
...
}

test.cpp

#include "lib.h"

int main() {
Add(4);
}

The compilation of test.cpp will not see the default parameter declaration, and will fail with an error.

For this reason, the default parameter definition is usually specified in the function declaration:

lib.h

int Add(int a, int b = 3);

C default arguments

Not really. The only way would be to write a varargs function and manually fill in default values for arguments which the caller doesn't pass.

Where to put default parameter value in C++?

Default parameter values must appear on the declaration, since that is the only thing that the caller sees.

EDIT: As others point out, you can have the argument on the definition, but I would advise writing all code as if that wasn't true.



Related Topics



Leave a reply



Submit