Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does "= default" allow out-of-line implementations?

Tags:

c++

c++11

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

Foo.h

#pragma once

class Foo
{
public:
    Foo() = default;

    Foo(const Foo& other) = default;
};

Purely out of curiosity, can the = default be used in the 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 compiles with gcc-5.1 but does the standard allow for this usage?

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

like image 943
James Adkison Avatar asked Apr 01 '16 13:04

James Adkison


People also ask

Can we have default implementations in the interface?

Interfaces can have default methods with implementation in Java 8 on later. Interfaces can have static methods as well, similar to static methods in classes. Default methods were introduced to provide backward compatibility for old interfaces so that they can have new methods without affecting existing code.

Do default methods need to be implemented?

The default methods are introduced in an interface since Java8. Unlike other abstract methods these are the methods can have a default implementation. If you have default method in an interface, it is not mandatory to override (provide body) it in the classes that are already implementing this interface.

What is a default implementation?

Default implementations help with that. An interface member can now be specified with a code body, and if an implementing class or struct does not provide an implementation of that member, no error occurs. Instead, the default implementation is used.

What does a default do to interface?

Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces. In particular, default methods enable you to add methods that accept lambda expressions as parameters to existing interfaces.


4 Answers

Yes this is legal. From [dcl.fct.def.default]

Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (12.1 12.4, 12.8), which might mean defining them as deleted. A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.

Emphasis mine

And then they go on to detail you exact scenario with

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

So as long as the function is not implicitly marked as deleted then the function will be defined where you explicitly default it.

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

The only "advantage" I can see it it allows existing code bases to change their cpp files to use modern techniques without having to change the header file. There is even a note in the standard:

Note: Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base.

like image 194
NathanOliver Avatar answered Sep 19 '22 05:09

NathanOliver


One potential usage of defaulting in the source file instead of the header is to use the pimpl idiom with unique_ptr. It requires a complete type for construction and destruction, so you can't define those special members in the header. You'd have 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;
like image 40
Barry Avatar answered Sep 18 '22 05:09

Barry


Yes, special member functions may be defaulted "out of line"; the compiler will generate the correct code and it will work as expected.

In fact, there is a rule relating to special members not being defaulted on first declaration, they are then considered user provided (and hence non-trivial).

A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.

Link here [dcl.fct.def.default]. With the following example detailing your situation;

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

Its usefulness lies in what it does, it provides the default implementation, but not at the point of declaration, thus making it user-provided. As noted, this is useful when dealing with an as-yet incomplete type, such as when using the pimpl idiom. It could also be used to mark your type as non-trivial, thus prohibiting its use in code that requires a trivial type (e.g. std::is_trivial).

like image 39
Niall Avatar answered Sep 22 '22 05:09

Niall


There is a small change in the behaviour. Other TU than Bar.cpp can't see that they are defaulted because they only see the header. So putting default in the cpp will make your class not trivially assignable and not trivially constructible.

There are some case that you want to do this: if your class hold a unique_ptr to an incomplete type, it's a good practice to default the destructor in the cpp, because if you don't, classes that uses yours will be required the incomplete type's destructor to be visible.

like image 26
Guillaume Racicot Avatar answered Sep 20 '22 05:09

Guillaume Racicot