Sorry, this might seem simple, but somebody asked me this, and I don't know for certain.
An empty C++ class comes with what functions?
Constructor,
Copy Constructor,
Assignment,
Destructor?
Is that it? Or are there more?
In C++03 there are 4:
Default constructor: Declared only if
no user-defined constructor is
declared. Defined when used
Copy constructor - declared only if the user hasn't declared one. Defined if used
Copy-assignment operator same as above
Destructor same as above
In C++11 there are two more:
Move constructor
Move-assignment operator
It is also possible that the compiler won't be able to generate some of them. For example, if the class contains, for example, a reference (or anything else that cannot be copy-assigned), then the compiler won't be able to generate a copy-assignment operator for you. For more information read this
If I define the following class
class X
{};
The compiler will define the following methods:
X::X() {} // Default constructor. It takes zero arguments (hence default).
X::~X() {} // Destructor
X::X(X const& rhs) {}; // Copy constructor
X& operator=(X const& rhs)
{return *this;} // Assignment operator.
// Since C++11 we also define move operations
X::X(X&& rhs) {}; // Move Constructor
X& operator=(X&& rhs)
{return *this;} // Move Assignment
Note:
The default constructor is not built if ANY constructor is defined.
The other methods are not built if the user defines an alternative.
What is slightly more interesting is the default implementation when we have members and a base:
class Y: public X
{
int a; // POD data
int* b; // POD (that also happens to be a pointer)
Z z; // A class
};
// Note: There are two variants of the default constructor.
// Both are used depending on context when the compiler defined version
// of the default constructor is used.
//
// One does `default initialization`
// One does `zero initialization`
// Objects are zero initialized when
// They are 'static storage duration'
// **OR** You use the braces when using the default constructor
Y::Y() // Zero initializer
: X() // Zero initializer
, a(0)
, b(0)
, z() // Zero initializer of Z called.
{}
// Objects are default initialized when
// They are 'automatic storage duration'
// **AND** don't use the braces when using the default constructor
Y::Y()
:X // Not legal syntax trying to portray default initialization of X (base class)
//,a // POD: uninitialized.
//,b // POD: uninitialized.
,z // Not legal syntax trying to portray default initialization of z (member)
{}
//
// Note: It is actually hard to correctly zero initialize a 'automatic storage duration'
// variable (because of the parsing problems it tends to end up a a function
// declaration). Thus in a function context member variables can have indeterminate
// values because of default initialization. Thus it is always good practice to
// to initialize all members of your class during construction (preferably in the
// initialization list).
//
// Note: This was defined this way so that the C++ is backward compatible with C.
// And obeys the rule of don't do more than you need too (because we want the C++
// code to be as fast and efficient as possible.
Y::Y(Y const& rhs)
:X(rhs) // Copy construct the base
,a(rhs.a) // Copy construct each member using the copy constructor.
,b(rhs.b) // NOTE: The order is explicitly defined
,z(rhs.z) // as the order of declaration in the class.
{}
Y& operator=(Y const& rhs)
{
X::operator=(rhs); // Use base copy assignment operator
a = rhs.a; // Use the copy assignment operator on each member.
b = rhs.b; // NOTE: The order is explicitly defined
z = rhs.z; // as the order of declaration in the class.
return(*this);
}
Y::~Y()
{
Your Code first
}
// Not legal code. Trying to show what happens.
: ~z()
, ~b() // Does nothing for pointers.
, ~a() // Does nothing for POD types
, ~X() ; // Base class destructed last.
// Move semantics:
Y::Y(Y&& rhs)
:X(std::move(rhs)) // Move construct the base
,a(std::move(rhs.a)) // Move construct each member using the copy constructor.
,b(std::move(rhs.b)) // NOTE: The order is explicitly defined
,z(std::move(rhs.z)) // as the order of declaration in the class.
{}
Y& operator=(Y&& rhs)
{
X::operator=(std::move(rhs)); // Use base move assignment operator
a = std::move(rhs.a); // Use the move assignment operator on each member.
b = std::move(rhs.b); // NOTE: The order is explicitly defined
z = std::move(rhs.z); // as the order of declaration in the class.
return(*this);
}
Just to expand on Armen Tsirunyan answer here are the signatures for the methods:
// C++03
MyClass(); // Default constructor
MyClass(const MyClass& other); // Copy constructor
MyClass& operator=(const MyClass& other); // Copy assignment operator
~MyClass(); // Destructor
// C++11 adds two more
MyClass(MyClass&& other) noexcept; // Move constructor
MyClass& operator=(MyClass&& other) noexcept; // Move assignment operator
Default methods assigned by compiler for a empty class:
http://cplusplusinterviews.blogspot.sg/2015/04/compiler-default-methods.html
Is that it?
Yes thats it.
Compiler generates by default
A default constructor
A copy constructor
A copy assignment operator
A destructor
for a class
You can see the default constructor, the copy constructor and the assignment operator being generated by default when you use -ast-dump option of Clang
prasoon#prasoon-desktop ~ $ cat empty.cpp && clang++ -cc1 -ast-dump empty.cpp
class empty
{};
int main()
{
empty e;
empty e2 = e;
{
empty e3;
e3 = e;
}
}
typedef char *__builtin_va_list;
class empty {
class empty;
inline empty() throw(); //default c-tor
//copy c-tor
inline empty(empty const &) throw() (CompoundStmt 0xa0b1050 <empty.cpp:1:7>)
//assignment operator
inline empty &operator=(empty const &) throw() (CompoundStmt 0xa0b1590 <empty.cpp:1:7>
(ReturnStmt 0xa0b1578 <col:7>
(UnaryOperator 0xa0b1558 <col:7> 'class empty' prefix '*'
(CXXThisExpr 0xa0b1538 <col:7> 'class empty *' this))))
};
Related
class A {
public:
A() {}
A(const A& a) { cout << "A::A(A&)" << endl; }
};
class B {
public:
explicit B(A aa) {}
};
int main() {
A a;
B b(a);
return 0;
}
Why does it print "A::A(A&)"?
When was the copy constructor for "A" called? And if the code calls the copy constructor, why can I remove the copy constructor without creating a compilation error?
B(A aa) takes an A by value, so when you execute B b(a) the compiler calls the copy constructor A(const A& a) to generate the instance of A named aa in the explicit constructor for B.
The reason you can remove the copy constructor and have this still work is that the compiler will generate a copy constructor for you in cases where you have not also declared a move constructor.
Note: The compiler generated copy constructor is often not sufficient for complex classes, it performs a simple member wise copy, so for complex elements or dynamically allocated memory you should declare your own.
§ 15.8.1
If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly.
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy
constructor is defined as deleted; otherwise, it is defined as defaulted (11.4). The latter case is deprecated if
the class has a user-declared copy assignment operator or a user-declared destructor or assignment operator.
Why the copy happens
Look at your class B c'tor:
class B {
public:
explicit B(A aa) {}
};
You receive A by value, triggering a copy during the call.
If you would have change it to (notice A & aa):
class B {
public:
explicit B(A & aa) {}
};
There wouldn't be any copy...
Default copy constructor
When you remove the c'tor, the compiler generates one for you when it can trivially do so:
First, you should understand that if you do not declare a copy
constructor, the compiler gives you one implicitly. The implicit
copy constructor does a member-wise copy of the source object.
The default c'tor is equivalent to:
MyClass::MyClass( const MyClass& other ) :
x( other.x ), c( other.c ), s( other.s ) {}
How to initialize function parameters or function return values when the type has no copy constructor and only explicit constructors? For example:
struct A { };
struct B {
B(const B&) = delete;
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return ...; // how to return a?
}
int main() {
A a;
foo( ... ); // how to pass a?
}
return a / foo(a) does not work because constructor B(const A&) is explicit. return B(a) / foo(B(a)) does not work because copy constructor is deleted.
My intention is to know the language rules. To me it looks like a flaw in the language that, considering the presented type B, I can initialize locals, statics and class members but apparently neither function parameters nor function return values.
B(const B&) = delete;, without an explicit definition of move-constructor, means the object cannot be copied or moved.
This means that you cannot pass by value, either as a parameter or as a return.
The explicit constructor is a red herring.
If you don't want to change B to enable copying or moving, then you will have to pass by reference.
answers in annotated code below:
struct A { };
struct B {
B(const B&) = delete;
B(B&&) = default; // how to return B - pt 1 : move constructor
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return B(a); // how to return B - pt 2 : explcitly convert it
}
int main() {
A a;
foo(B(a)); // how to pass a - explicit conversion
}
I think your issue is a misconception:
You do not initialize function parameters or return values with the constructor.
Instead you hand in parameters to a function, handing in means copying them.
Depending on the function signature either the variable itself is copied (void foo(B b)) or the reference/pointer to it is copied.
In the first case you need a copy constuctor for your type, while you do not need one for references/pointers.
The same is true for returning values (!) from a function.
You also need a copy constructor there.
But you deleted the copy constructor, thus the compile errors.
With C++11 move semantics were added as an optimisation opportunity.
When a move constructor is available r-values will be moved in/out of functions automatically instead of copied.
Returning a local variable will also move it out instead of copying.
Since you declared a copy consturctor (no matter if as deleted/default) no move related functions will be created by the compiler.
And thus also the automatic moving fails.
I'm busy testing an implementation of various generic algorithms and I'm using types with minimal support of provided functions. I came across this weird setup when using a std::pair<T, movable> with some type T (e.g., int) and a movable type defined like this:
struct movable
{
movable() {}
movable(movable&&) = default;
// movable(movable const&) = delete;
movable(movable&) = delete;
};
The idea is have a type which is movable but not copyable. That works great, e.g., with expressions like this:
movable m1 = movable();
movable m2 = std::move(m1);
However, when trying to use this type as a member of std::pair<...> it fails! To make get the code to compile it is necessary to add the deleted(!) copy constructor taking a movable const& (or have only that version). The copy constructor taking a non-const reference is insufficient:
#include <utility>
auto f() -> std::pair<int, movable> {
return std::pair<int, movable>(int(), movable());
}
What is going on here? Is std::pair<...> overspecified by mandating that std::pair(std::pair const&) is = defaulted?
The problem seems to be down to the specification of std::pair's copy constructor (in 20.3.2 [pairs.pair] synopsis):
namespace std {
template <class T1, class T2>
struct pair {
...
pair(const pair&) = default;
...
};
}
A quick check with my implementation implies that the obvious implementation copying the two members does not require the const& version of the movable copy constructor. That is, the offensive part is the = default on pair's copy constructor!
std::pair copy constructor is declared as follows:
pair(const pair&) = default;
By declaring this copy constructor for movable:
movable(movable&) = delete;
you inhibit implicit creation of movable(const movable&) (so it's not even deleted, there's just no such constructor), thus this is the only copy constructor you have. But std::pair copy constructor requires a copy constructor of its members to take const reference, so you get compile error.
If you add this:
movable(movable const&) = delete;
or (better) just remove movable(movable&) = delete; declaration, you now have the movable(movable const&) constructor, and because it's deleted, the std::pair copy constructor also becomes deleted.
Update: Let's consider a simpler example demonstrating the same issue. This doesn't compile:
template <typename T>
struct holder {
T t;
// will compile if you comment the next line
holder(holder const&) = default;
// adding or removing move constructor changes nothing WRT compile errors
// holder(holder&&) = default;
};
struct movable {
movable() {}
movable(movable&&) = default;
// will also compile if you uncomment the next line
//movable(movable const&) = delete;
movable(movable&) = delete;
};
holder<movable> h{movable()};
It will compile if you comment the copy constructor of holder, because this is how implicit copy constructor generation works ([class.copy]/8:
The implicitly-declared copy constructor for a class X will have the form
X::X(const X&)
if each potentially constructed subobject of a class type M (or array thereof) has a copy constructor whose first parameter is of type const M& or const volatile M&. Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
That is, when you comment out the declaration holder(holder const&) = default; the implicitly declared copy constructor of holder will have the form holder(holder&). But if you don't, T's copy constructor has take const T& (or const volatile T&) because this is what will be called in memberwise copy procedure described in [class.copy]/15.
And if holder has a move constructor, it's even easier - if you comment out holder(holder const&) = default;, the implicitly declared copy constructor of holder will be just deleted.
I'm familiar with the principle (for example, from this answer and this one) that when a class has a move constructor and/or move assignment operator, its default copy constructor and copy assignment operator are deleted. However, In the examples I've seen, this can be addressed by explicitly defining a new copy constructor and assignment operator.
In my particular case, I have a class which is derived by joint inheritance from a C-style struct and a template class. The copy and move assignment operators are explicitly defined in the template, whilst the copy and move constructors are explicitly defined in the class itself. In other words, everything is defined explicitly, though not all in the same place. Here is some example code:
typedef struct {
int n;
} myStruct;
template <typename T> class myTemplate
{
public:
// Default constructor
myTemplate<T>() : t_n(nullptr) {}
// Cannot create copy or move constructors in template, as cannot
// access the 'n' member directly
// Copy assignment operator
myTemplate<T> & operator=(const myTemplate<T> &source)
{
if (this != &source)
{
*t_n = *(source.t_n);
}
return *this;
}
//! Move assignment operator
myTemplate<T> & operator=(myTemplate<T> &&source)
{
if (this != &source)
{
*t_n = *(source.t_n);
*(source.t_n) = 0;
source.t_n = nullptr;
}
return *this;
}
T* t_n;
};
class myClass : public myStruct, public myTemplate<int>
{
public:
// Default constructor
myClass() : myTemplate<int>()
{
n = 0;
t_n = &n;
}
// Alternative constructor
myClass(const int &n_init) : myTemplate<int>()
{
n = n_init;
t_n = &n;
}
// Copy constructor
myClass(const myClass &source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
}
// Move constructor
myClass(myClass &&source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
source.n = 0;
source.t_n = nullptr;
}
};
int main()
{
myClass myObject(5);
myClass myOtherObject;
// Compilation error here:
myOtherObject = myObject;
return 1;
}
In Visual C++ and Intel C++ on Windows, this works exactly as I'd expect it to. On gcc 4.9.0 in Linux, however, I get the dreaded error message:
g++ -c -std=c++11 Main.cppMain.cpp: In function ‘int main()’:
Main.cpp:78:19: error: use of deleted function ‘myClass& myClass::operator=(const myClass&)’
myOtherObject = myObject;
^
Main.cpp:39:7: note: ‘myClass& myClass::operator=(const myClass&)’ is implicitly declared as deleted because ‘myClass’ declares a move constructor or move assignment operator
class myClass : public myStruct, public myTemplate<int>
Sure enough, the error goes away if I define an explicit copy assignment operator in the class itself, rather than in the template, but that's bothersome to do and undermines the advantage of using the template, since (a) my actual copy assignment operator is a lot bigger than the one shown here and (b) there are a large number of different classes that all share this template.
So, is this simply a bug in gcc 4.9.0, or is this in fact what the standard says should happen?
GCC is correct (and Clang and EDG agree).
myTemplate has a user-declared move constructor, therefore its copy assignment operator is deleted.
You've provided a copy constructor, but not copy assignment operator. Just declare a copy assignment operator for myTemplate and define it as defaulted. That takes one extra line of code.
I have some questions about constructors in C++. For each question (from (1) to (4)) I would like to know if the behaviour is perfectly defined regarding to the standard.
A) The first one is about initialization of members :
class Class
{
public:
Class()
: _x(0)
, _y(_x)
, _z(_y) // <- (1) Can I initialize a member with other members ?
{
;
}
protected:
int _x;
int _y;
int _z;
};
B) What are the functions added to each class by the compiler ?
template<typename T> class Class
{
public:
template<typename T0>
Class(const Class<T0>& other)
{
std::cout<<"My copy constructor"<<std::endl;
_x = other._x;
}
template<typename T0 = T>
Class (const T0 var = 0)
{
std::cout<<"My normal constructor"<<std::endl;
_x = var;
}
public:
T _x;
};
// (2) Are
// Class(const Class<T>& other)
// AND
// inline Class<T>& operator=(const Class<T>& other)
// the only functions automatically added by the compiler ?
As an example, if I call :
Class<int> a;
Class<int> b(a); // <- Will not write "My copy constructor"
Class<double> c(a); // <- Will write "My copy constructor"
(3) Is this behaviour perfectly normal according to the standard ?
(4) Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?
Can I initialize a member with other members ?
Yes, as long as those other members have already been initialised; i.e. as long as their declarations come before the member being initialised.
Are [the copy constructor] and [the copy-assignment operator] the only functions automatically added by the compiler ?
It will also implicitly declare a destructor, which will destroy _x using its destructor.
In C++11, a move constructor (Class(Class&&)) and move-assignment operator (Class& operator=(Class&&)) are also implicitly declared, unless you declare a copy or move constructor, or a copy or move assignment operator.
Note that your constructor template is not a copy constructor, and the implicit one will be used instead:
Class<T1> t1;
Class<T1>(t1); // prints nothing
Class<T2>(t1); // prints "My copy constructor" (which is a lie)
Is this behaviour perfectly normal according to the standard ?
Yes, see chapter 12.
Do I have the guarantee that an empty constructor is not automatically added and that Class<int> x; will write "My normal constructor" ?
Yes, a default constructor will only be implicitly declared if you don't declare any constructors at all.