What Are All the Member-Functions Created by Compiler For a Class? Does That Happen All the Time

What are all the member-functions created by compiler for a class? Does that happen all the time?

C++98/03

If they are needed,

  1. the compiler will generate a default constructor for you unless you declare any constructor of your own.
  2. the compiler will generate a copy constructor for you unless you declare your own.
  3. the compiler will generate a copy assignment operator for you unless you declare your own.
  4. the compiler will generate a destructor for you unless you declare your own.

As Péter said in a helpful comment, all those are only generated by the compiler when they are needed. (The difference is that, when the compiler cannot create them, that's Ok as long as they aren't used.)


C++11

C++11 adds the following rules, which are also true for C++14 (credits to towi, see this comment):

  • The compiler generates the move constructor if

    • there is no user-declared copy constructor, and
    • there is no user-declared copy assignment operator, and
    • there is no user-declared move assignment operator and
    • there is no user-declared destructor,
    • it is not marked deleted,
    • and all members and bases are moveable.
  • Similarly for move assignment operator, it is generated if

    • there is no user-declared copy constructor, and
    • there is no user-declared copy assignment operator, and
    • there is no user-declared move constructor and
    • there is no user-declared destructor,
    • it is not marked deleted,
    • and all members and bases are moveable.

Note that these rules are a bit more elaborate than the C++03 rules and make more sense in practice.

For an easier understanding of what is what in the above:

class Thing {
public:
Thing(); // default constructor
Thing(const Thing&); // copy c'tor
Thing& operator=(const Thing&); // copy-assign
~Thing(); // d'tor
// C++11:
Thing(Thing&&); // move c'tor
Thing& operator=(Thing&&); // move-assign
};

Further reading: if you are a C++-beginner consider a design that does not require you to implement any of five a.k.a The Rule Of Zero originally from an article written by Martinho Fernandes.

Does generation of implicitly defined member functions get handled independently for every class in a class hierarchy in C++?

The compiler will always declare a destructor for a class C that has no user-declared destructor (C++17 [class.dtor]/4). However, if any base class of C has a deleted or inaccessible constructor, then C's destructor will also be declared as deleted (p5).

A similar statement holds for copy constructors ([class.copy.ctor]/6) and copy assignment operators ([class.copy.assign]/2).

In the case of a move constructor, the compiler will only implicitly declare it for a class C if the user did not declare any Rule of 5 functions for C ([class.copy.ctor]/8). The base classes of C can affect whether the move constructor of C is deleted or not, but they do not affect whether the compiler generates a declaration. A similar statement holds for move assignment operators ([class.copy.assign]/4).

How does the copy constructor work in this code?

y is an object of class foo, so why doesn't the compiler call the constructor and then copy the contents of x into y?

It does.

It calls the copy constructor, to do exactly that.

Where does the default copy-constructor provided by the compiler fit in this picture?

You didn't provide your own copy constructor which produces any output, so the automatically-generated one (which doesn't) gets called.

The default constructor (the one that looks like foo()) doesn't get invoked at all during a copy. Only the copy constructor (the one that looks like foo(const foo&)).

Only one constructor happens per construction. Well, unless you're using delegating constructors, but we'll talk about that another day. /p>



#include <iostream>
using namespace std;

class foo
{
static int objCount;
public:
foo()
{
objCount++;
cout<<"constructor :"<<foo::objCount<<endl;
}
foo(const foo&)
{
objCount++;
cout<<"copy constructor :"<<foo::objCount<<endl;
}
~foo()
{
objCount--;
cout<<"destructor :"<<foo::objCount<<endl;
}

};

int foo::objCount=0;

int main()
{
foo x;
foo y = x;
return 0;
}

// constructor :1
// copy constructor :2
// destructor :1
// destructor :0

Passing class to constructor, when no such constructor exists. Why does it work?

The compiler generates a copy constructor for you:

If no user-defined copy constructors are provided for a class type (struct, class, or union), the compiler will always declare a copy constructor as a non-explicit inline public member of its class.

You can make the copy constructor and assignment deleted and make the compiler not declare move assignment and constructor by declaring one of move constructor or assignment as deleted:

A(A&&) = delete; // Makes the class non-copyable and non-moveable.

What compiler will give for us for empty class in C++?

The answer depends a lot on how the class is used and which version of C++ is involved:

  • The class will have non-zero size, so that pointer arithmetic works and the elements of arrays of them will be distinct.

    assert(sizeof(empty_class_t) != 0)
  • The class will have a default constructor, if you construct any.

    empty_class_t e;
  • The class will have a default copy constructor, if you copy any.

    empty_class_t f(e);
  • The class will have a default copy assignment operator, if you assign any.

    empty_class_t g = f;
  • In C++11, the class will have a default move constructor, if you use it.

    empty_class_t h(std::move(g));
  • In C++11, the class will have a default move assignment operator, if you use it.

    empty_class_t j; j = std::move(h);

I think I've got all the uses right - someone let me know of any booboos, please.

See this answer for details of when these are generated for classes in general, but basically they are only generated if the compiler can do something sensible and they are used in your code; the compiler is not required to fail where one of these default members cannot be generated but is not used.

Location of compiler-generated class methods

tl;dr

  • Implicitly declared destructors are declared public inline and at the end of the class definition, e.g.:
    struct A {
    std::string s;
    public: inline ~A(); // implicit destructor
    };
  • Destructors only get defined when they're used in the current translation unit.
  • The Destructor Body will be able to use all types that were accessible at the class definition and additionally all types that were accessible at the point were the class was first used.
  • How the compiler generates the code for the destructor and where it'll ultimately end up with is not easily answerable, since it depends on the compiler, compilation flags and even the code that gets compiled.

Long Explanation

What the C++ Standard says

  • 11.4.7 Destructors

    If a class has no user-declared prospective destructor, a prospective destructor is implicitly declared as
    defaulted (9.5). An implicitly-declared prospective destructor is an inline public member of its class.

  • 11.4.4 Special member functions

    Default constructors (11.4.5.2), copy constructors, move constructors (11.4.5.3), copy assignment operators,
    move assignment operators (11.4.6), and prospective destructors (11.4.7) are special member functions.
    An implicitly-declared special member function is declared at the closing } of the class-specifier. Programs
    shall not define implicitly-declared special member functions.

So if you don't add a destructor to your class you'll get an implicit one that will be an inline public member of the class and declared at the the end of the class definition.

Note that it is only declared - not defined - so the compiler will not check if the destructor would compile at this point.

i.e. in your example it could look like this:

struct A {
std::string s;

// it's only *declared*, not defined
inline ~A();
};

The compiler only needs to define the destructor if it is used somewhere.

So unless you use A somewhere it's implicit destructor will never be defined and you won't get any errors.

So the following is valid c++ even though the destructor of Foo would not be able to be compiled because Miau is never defined:

struct Miau;

class Foo {
public:
std::unique_ptr<Miau> miauPtr;
};

// Miau never defined
// Foo never used

The actual point where the compiler needs to define the destructor is:

  • 11.4.7 Destructors

    A destructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.3) or
    when it is explicitly defaulted after its first declaration.

So the compiler needs to generate a definition for the destructor when you actually use it or when you explicitly default it (e.g. A::~A() = default;, which in your case doesn't apply)

At this point you would actually get an error if your implicit destructor doesn't compile (e.g. because one of the members uses an incomplete type)

The declarations that will be visible to the implicitly defined destructor are defined as follows:

  • 10.6 Instantiation context

    During the implicit definition of a defaulted function (11.4.4, 11.11.1), the instantiation context is the union
    of the instantiation context from the definition of the class and the instantiation context of the program
    construct that resulted in the implicit definition of the defaulted function.

So everything that was accessible at the point of the class definition and additionally everything that is accessible from the point where you first used the class is visible to the destructor.

e.g.:

#include <memory>
#include <string>

struct Miau;

struct Foo {
std::unique_ptr<Miau> miauPtr;
};

struct Miau { std::string s; };


void useFoo() {
Foo f; // first usage of Foo
// this forces the compiler to define the destructor for Foo.
// it'll compile without any error because at this point Miau
// is already defined.
}

godbolt example

Note that this is only the case for implicitly defined destructors.

If you explicitly define the destructor (even if you default it), then instead of the point of first use the point where you explicitly defined it will be used instead.

e.g. if you replace Foo from the example above with:

// ok - implicit declaration and implicit definition
struct Foo {
std::unique_ptr<Miau> miauPtr;
};

// ok - explicit declaration and implicit definition
struct Foo {
~Foo() = default;
std::unique_ptr<Miau> miauPtr;
};

// error - explicit declaration and explicit definition
struct Foo {
~Foo();
std::unique_ptr<Miau> miauPtr;
};

Foo::~Foo() = default; // if Miau is not defined before this line we'll get an error

godbolt example

What's up to the compilers

The standard only defines how the destructor will be declared in the class body and when it needs to be defined.

Everything else is the decision of the compiler - how he generates the code for the destructor (be it source code, some internal representation or directly bytecode) or where the code then gets stored.

So you might up with no code at all for the destructor when it gets inlined everywhere.
Or you might get one version of the destructor for each translation unit.
Or a single version of the destructor that gets called by all the translation units.

This might also depend on the compilation flags you use and even on the code itself - so without a concrete example there is no clear answer to what the compiler will do with the implicit destructor.

The only guarantee you have is that the code of the destructor will get executed when an A is destroyed, apart from that everything is in the hands of the compiler.



Related Topics



Leave a reply



Submit