Does "= default" allow non-implementation implementations?

I usually see the syntax = default used in the header. I understand that this is the same as if the functions are explicitly implemented in the header, see below Foo .

foo.h

 #pragma once class Foo { public: Foo() = default; Foo(const Foo& other) = default; }; 

Out of curiosity, can = default be used in source files as follows?

bar.h

 #pragma once class Bar { public: Bar(); Bar(const Bar& other); }; 

Bar.cpp

 #include "Bar.h" Bar::Bar() = default; Bar::Bar(const Bar&) = default; 

As far as I know, this is equivalent to explicitly implementing the functions in the cpp file.

The above Bar example is compiled using gcc-5.1 , but does this standard allow this use?

As an aside, are there any advantages to using = default in the source file compared to the header?

+6
source share
5 answers

Yes, it is legal. From [dcl.fct.def.default]

Explicit default functions and implicitly declared functions are collectively called default functions, and the implementation must contain implicit definitions for them (12.1, 12.4, 12.8), which may mean their removal. A function is provided by the user if it is declared by the user and is clearly not defaulted or deleted when it was first declared. The user-provided function of explicitly default value (i.e., obviously defaulted after its first declaration) is defined in the place where it is clearly defaulted; if such a function is implicitly defined as remote, the program is poorly formed.

Emphasis on mine

And then they detail the exact scenario with

 struct nontrivial1 { nontrivial1(); }; nontrivial1::nontrivial1() = default; // not first declaration 

So, until the function is marked as implicit, as indicated, the function will be defined where you are explicitly the default.

As an aside, are there any advantages to using = default in the source file compared to the header?

The only β€œadvantage” I see is that it allows existing code bases to modify their cpp files to use modern technology without changing the header file. There is even a note in the standard:

Note. Declaring a default function after its first declaration can provide efficient execution and a brief definition, allowing a stable binary interface to develop a changing code base.

+4
source

One possible way to use defaults in the source file instead of the header is to use the pimpl idiom with unique_ptr . Building and destroying requires a full type, so you cannot define these special elements in the header. You will need to do:

foo.h

 struct Foo { struct Impl; unique_ptr<Impl> p; Foo(); ~Foo(); }; 

foo.cpp

 // Foo::Impl definition here // now Impl isn't incomplete Foo::Foo() = default; Foo::~Foo() = default; 
+3
source

Yes, special member functions can default out of turn; the compiler will generate the correct code and it will work as expected.

In fact, there is a rule regarding special members who were not defaulted on the first announcement, then they are considered user-provided (and therefore non-trivial).

A function is provided by the user if it is declared by the user and is clearly not defaulted or deleted when it was first declared. The user-provided function of explicitly default value (i.e., explicitly defaulted after the first declaration) is defined in the place where it is clearly defaulted; if such a function is implicitly defined as remote, the program is poorly formed.

Link here [dcl.fct.def.default] . The following example details your situation;

 struct nontrivial1 { nontrivial1(); }; nontrivial1::nontrivial1() = default; // not first declaration 

Its usefulness is what it does, it provides a default implementation, but not at the point of declaration, which makes it accessible to the user. As already noted, this is useful when working with a still incomplete type, for example, when using the pimpl idiom. It can also be used to designate your type as nontrivial, which prohibits its use in code that requires a trivial type (for example, std::is_trivial ).

+2
source

There is a slight change in behavior. Other Bar.cpp than Bar.cpp cannot see that they are by default because they only see the title. Thus, default in cpp will make your class not trivially assignable, not trivially constructive.

There is one case that you want to do: if your class contains unique_ptr for an incomplete type, it is good practice to default the destructor in cpp, because if you do not, classes that use yours will require the destructor of the incomplete type to be visible.

+1
source

When implemented in the source file, the methods are no longer defaults, but provided by the user.

0
source

All Articles