Classes Compiler Synthesized Functions
Classes Compiler Synthesized Functions
For more information, see this Microsoft page on explicitly defaulted and deleted functions.
1 class Empty {
2 // empty by design
3 };
1 class Empty {
2 public:
3 Empty(); // default constructor
4 Empty(Empty const& rhs); // copy constructor
5 ~Empty(); // destructor
6 Empty& operator=(Empty const& rhs); // assignment operator
7 Empty* operator&(); // address-of operators
8 Empty const* operator&() const;
9 };
These functions are generated only if they are needed, but it doesn't take much to need them.
The following code will cause each function to be generated:
Given that compilers are synthesizing functions for you, what do these functions do? Well, the
default constructor and the destructor don't really do anything. They just enable you to create
and destroy objects of the class. The default address-of operators just return the object's address.
These functions are effectively defined like this:
1/6
Compiler-Synthesized Functions [Prasanna Ghali]
1 inline Empty::Empty() {}
2 inline Empty::~Empty() {}
3 inline Empty* Empty::operator&() { return this; }
4 inline Empty const* Empty::operator&() const { return this; }
As for the copy constructor and the assignment operator, the official rule is this: the default copy
constructor (assignment operator) performs member-wise copy construction (assignment) of non-
static data members of the class. That is, if m is a non- static data member of type T in a class
C and C declares no copy constructor (assignment operator), m will be copy constructed
(assigned) using the copy constructor (assignment operator) defined for T , if there is one. If there
isn't, this rule will be recursively applied to m 's data members until a copy constructor
(assignment operator) or built-in type (e.g., int , double , pointer, etc.) is found. By default,
objects of built-in types are copy constructed (assigned) using bitwise copy from the source object
to the destination object.
default ed functions
Here's an example to understand how synthesized functions behave. Consider the definition of a
NamedInt class, whose instances are classes allowing you to associate names with int values:
1 class NamedInt {
2 public:
3 NamedInt(char const *name, int value);
4 NamedInt(std::string const& name, int value);
5 // we tell human readers that by design a copy ctor and op=
6 // must be generated:
7 NamedInt(NamedInt const&) = default;
8 NamedInt& operator=(NamedInt const&) = default;
9 // other stuff here that is not relevant to our discussion
10 private:
11 std::string nameValue;
12 int intValue;
13 };
You can explicitly ask the compiler to generate the synthesized versions of the copy-
control members by declaring them as = default .
Because the NamedInt classes declare at least one constructor, compilers won't generate default
constructors, but because the programmer has explicitly asked for the synthesized versions, the
compiler will generate those functions [if they're needed]. Note that if classes fail to declare copy
constructors or assignment operators, compilers will generate those functions [if they are
needed]. Consider the following call to a copy constructor:
The copy constructor generated by the compiler must initialize no2.nameValue and
no2.intValue using no1.nameValue and no1.intValue , respectively. The type of nameValue is
string , and string has a copy constructor [which you can verify by examining string in the
standard library], so no2.nameValue will be initialized by calling the string copy constructor
with no1.nameValue as its argument. On the other hand, the type of NamedInt::intValue is
2/6
Compiler-Synthesized Functions [Prasanna Ghali]
int , and no copy constructor is defined for int s, so no2.intValue will be initialized by copying
the bits over from no1.intValue .
The code for class NamedInt and to test the keyword default can be found here.
delete d functions
The compiler-generated assignment operator for NamedInt would behave the same way, but in
general, compiler-generated assignment operators behave as described only when the resulting
code is both legal and has a reasonable chance of making sense. If either of these tests fails,
compilers will refuse to generate an operator= for your class.
For example, suppose a class NamedRefIntConst were defined like this, where nameValue is a
reference to a string and intValue is a int const :
1 class NamedRefIntConst {
2 public:
3 // this ctor no longer takes a const name, because nameValue is
4 // now a reference-to-non-const string. The char const* ctor is
5 // gone, because we must have a string to refer to
6 NamedRefIntConst(std::string& name, int value);
7 // assume no copy ctor nor operator= is declared ...
8 // other stuff here that is not relevant to our discussion
9 private:
10 std::string& nameValue; // this is now a reference
11 int const intValue; // this is now const
12 };
1 std::string newCar("Beemer");
2 std::string oldCar("Porsche");
3 NamedRefIntConst p(newCar, 2); // I've owned this car for 2 years
4 NamedRefIntConst s(oldCar, 5); // I've owned this car for 5 years
5 // what should happen to the data members in p?
6 p = s;
Before the assignment, p.nameValue refers to some string object and s.nameValue also refers
to a string , though not the same one. How should the assignment affect p.nameValue ? After
the assignment, should p.nameValue refer to the string referred to by s.nameValue , i.e.,
should the reference itself be modified? However, that is impossible, because C++ doesn't provide
a way to make a reference refer to a different object. Alternatively, should the string object to
which p.nameValue refers be modified, thus affecting other objects that hold pointers or
references to that string , i.e., objects not directly involved in the assignment? Is that what the
compiler-generated assignment operator should do?
Faced with such a conundrum, C++ refuses to compile the code. If you want to support
assignment in a class containing a reference member, you must define the assignment operator
yourself. Compilers behave similarly for classes containing const members [such as intValue in
class NamedRefIntConst ]; it's not legal to modify const members, so compilers are unsure how
to treat them during an implicitly generated assignment function.
In the case of class NamedRefIntConst , the programmer can explicitly disallow use of compiler-
generated copy constructor and assignment operator using keyword delete :
3/6
Compiler-Synthesized Functions [Prasanna Ghali]
1 class NamedRefIntConst {
2 public:
3 // this ctor no longer takes a const name, because nameValue is
4 // now a reference-to-non-const string. The char const* ctor is
5 // gone, because we must have a string to refer to
6 NamedRefIntConst(std::string& name, int value);
7
8 // explicitly disallow any type of copy operation!!!
9 NamedRefIntConst(NamedRefIntConst const&) = delete;
10 NamedRefIntConst& operator=(NamedRefIntConst const&) = delete;
11 // other stuff here that is not relevant to our discussion
12 private:
13 std::string& nameValue; // this is now a reference
14 int const intValue; // this is now const
15 };
4/6
Compiler-Synthesized Functions [Prasanna Ghali]
5/6
Compiler-Synthesized Functions [Prasanna Ghali]
6/6