How to understand the differences between direct initialization and copy initialization [duplicate] - c++

This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 6 years ago.
The following is excerpted from section 13.1.1. from book "C++ Prime", 5th edition:
To verify the above paragraph, especially the statement underlined with red, I wrote the following codes:
#include<iostream>
using namespace std;
class TestClass {
public:
TestClass() :a(7) {
cout << "Default constructor";
}
TestClass(int aa) : a(aa) {
cout << "Constructor" << endl;
}
TestClass(const TestClass & t): a(t.a) {
cout << "Copy constructor" << endl;
}
TestClass & operator=(const TestClass & rhs) {
a = rhs.a;
return *this;
}
int a;
};
int main() {
TestClass t1(1);
TestClass t2 = t1;
}
Based on my understanding of the description of copy initialization in the book, the code should first create t2 using default initializer, then use operator= function to copy the right-hand operand t1. But when I debug line by line in Visual Studio 2015, the code go straight to the copy constructor TestClass(const TestClass & t). This shows that direct initialization and copy initialization are actually doing the same thing, no difference. So, is my understanding wrong or the book is wrong? If I am wrong, what is the correct understanding of the difference between direct initialization and copy initialization? Could you please give me an example code to show such difference? Thanks a lot.
Edit: someone says my question can be answered in this thread. But that thread is only a (detailed and lengthened) repeat of the text I excerpted. It doesn't answer why in practice (e.g., Visual Studio 2015) it is not true.

The book just says "copy", which doesn't just mean copy assignment. Note the word "created", copy initialization means construction, not assignment.
For TestClass t2 = t1;, t2 will be copy constructed from t1 via copy constructor directly, not default construction and then assignment.
If T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.
Yes, copy initialization and direct initialization have the same effect in most cases, but there's a difference between them.
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
e.g.
class TestClass {
public:
// the copy constructor is declared explicit now
explicit TestClass(const TestClass & t): a(t.a) {
cout << "Copy constructor" << endl;
}
TestClass(int aa) : a(aa) {
cout << "Constructor" << endl;
}
int a;
};
then
int main() {
TestClass t0(1);
TestClass t1(t0); // fine; explicit constructor works fine with direct initialization
TestClass t2 = t0; // error; explicit constructor won't be considered for copy initialization
}

Related

Cannot use class constructor using equals sign notation when class copy constructor is deleted

While testing some classes I run into an interesting problem: When invoking class constructor using the equals sign notation (=) if the copy constructor is deleted an error is encountered error: copying variable of type 'Class' invokes deleted constructor. While using the parenthesis the code compiles normally.
What is going on here? Can this be a compiler bug?
Consider the following class:
class Test
{
public:
int Int;
public:
Test() = default;
Test(Test &) = delete;
Test(Test &&) = delete;
Test(int i)
{
Int = i;
}
};
Constructors invoked like so:
Test t1(3); //No error
Test t2 = 3; //error: copying variable of type 'Class' invokes deleted constructor
Just to check I tried to add some checks and allow these functions and compile the code.
Both constructors compiled using MSVC in the exact same way.
class Test
{
public:
int Int;
public:
Test()
{
Int = 0;
cout << "Constructor";
}
Test(Test &t)
{
Int = t.Int;
cout << "Copy Constructor";
}
Test(Test &&t)
{
Int = t.Int;
cout << "Move Constructor";
}
Test(int i)
{
Int = i;
cout << "Constructor from int";
}
};
Test t1(3); //Constructor from int
Test t2 = 3; //Constructor from int
What exactly is going on here?
You're seeing the results of the copy elision rule.
Basically, saying T var = expr; constructs an unnamed temp from expr and then copies or moves it into var using the copy or move construtor. If the copy and move constructors are deleted, then this gives an error about the deleted constructor. But then, the compiler is required to elide that copy or move and construct var directly from expr, even if the copy or move constructor has visible side effects. Its one of those weird corner cases that arises from language design by de-facto standardization of what disparate implementations do (or did at some time in the past), plus design-by-committee and slow evolution over time while trying to keep backwards compatibility.
see here for more discussion

Rule of 5 - Compiles without assignment operator [duplicate]

I wrote the following program to test when the copy constructor is called and when the assignment operator is called:
#include
class Test
{
public:
Test() :
iItem (0)
{
std::cout << "This is the default ctor" << std::endl;
}
Test (const Test& t) :
iItem (t.iItem)
{
std::cout << "This is the copy ctor" << std::endl;
}
~Test()
{
std::cout << "This is the dtor" << std::endl;
}
const Test& operator=(const Test& t)
{
iItem = t.iItem;
std::cout << "This is the assignment operator" << std::endl;
return *this;
}
private:
int iItem;
};
int main()
{
{
Test t1;
Test t2 = t1;
}
{
Test t1;
Test t2 (t1);
}
{
Test t1;
Test t2;
t2 = t1;
}
}
This results in the following output (just added empy lines to make it more understandable):
doronw#DW01:~$ ./test
This is the default ctor
This is the copy ctor
This is the dtor
This is the dtor
This is the default ctor
This is the copy ctor
This is the dtor
This is the dtor
This is the default ctor
This is the default ctor
This is the assignment operator
This is the dtor
This is the dtor
The second and third set behave as expected, but in the first set the copy constructor is called even though the assignment operator is used.
Is this behaviour part of the C++ standard or just a clever compiler optimization (I am using gcc 4.4.1)
No assignment operator is used in the first test-case. It just uses the initialization form called "copy initialization". Copy initialization does not consider explicit constructors when initializing the object.
struct A {
A();
// explicit copy constructor
explicit A(A const&);
// explicit constructor
explicit A(int);
// non-explicit "converting" constructor
A(char const*c);
};
A a;
A b = a; // fail
A b1(a); // succeeds, "direct initialization"
A c = 1; // fail, no converting constructor found
A d(1); // succeeds
A e = "hello"; // succeeds, converting constructor used
Copy initialization is used in those cases that correspond to implicit conversions, where one does not explicitly kick off a conversion, as in function argument passing, and returning from a function.
C++ standard 8.5/12
The initialization that occurs in
argument passing, function return,
throwing an exception (15.1), handling
an exception (15.3), and
brace-enclosed initializer lists
(8.5.1) is called copy-initialization
and is equivalent to the form
T x = a;
The initialization that occurs in new
expressions (5.3.4), static_cast
expressions (5.2.9), functional
notation type conversions (5.2.3), and
base and member initializers (12.6.2)
is called direct-initialization and is
equivalent to the form
T x(a);
Your first set is according to the C++ standard, and not due to some optimization.
Section 12.8 ([class.copy]) of the C++ standard gives a similar example:
class X {
// ...
public:
X(int);
X(const X&, int = 1);
};
X a(1); // calls X(int);
X b(a, 0); // calls X(const X&, int);
X c = b; // calls X(const X&, int);
The last line would be the one matching your case.

Difference between && and no ref in return type [duplicate]

This question already has an answer here:
Difference between "return-by-rvalue-ref" & "return-by-value" when you return using std::move?
(1 answer)
Closed 6 years ago.
Is there a difference in behavior when the return type is explicitly declared rvalue vs no ref? According to the example below, there doesn't seem to be any difference.
#include <iostream>
#include <vector>
using namespace std;
struct A {
A(int x) : x_(x) {}
A(A&&) = default; // VC12 hasn't implemented default move
A(const A&) = delete;
A& operator=(A&&) = default;
A& operator=(const A&) = delete;
vector<int> x_;
};
struct B{
B(int x) : a_(x) {}
A&& foo1() { return move(a_); } // explicitly declared as rvalue
A foo2() { return move(a_); } // no ref
A a_;
};
int main() {
B b1(7);
A a1 = b1.foo1();
B b2(7);
A a2 = b2.foo2();
cout << a1.x_.size() << ' ' << a2.x_.size() << endl;
cout << b1.a_.x_.size() << ' ' << b2.a_.x_.size() << endl;
return 0;
}
This example has been compiled by Ideone's C++14 compiler (not sure of the exact version, I suspect it's gnu 5.1) and VC12 (Visual Studio 2013). The only minor difference is VC12 requires an explicit move implementation.
Edit: A related SO post said that the two function end up doing the same thing. However, "in many cases it allows the compiler to perform copy elision and elide the calls to the move constructor of the returned type, as permitted by paragraph 12.8/31 of the C++11 Standard". "Copy elision allows the compiler to create the return value of the function directly in the object."
Question 1: Copy elision should still happen when move is explicitly called, right?
Question 2:
When move is explicitly called on a lvalue (so a required called), A&& and A means the same behavior. When move is not explicitly called, meaning the compiler performs copy elision, A should be the only return type. Combining the two scenario above, can I conclude that return type A&& is not useful and only adds confusion?
With
A&& foo1() { return move(a_); }
A foo2() { return move(a_); }
foo1 returns a (rvalue) reference.
foo2 construct an object A with the move constructor.

Constructor being called multiple times

I wrote the following c++ code trying to understand copy elision in c++.
#include <iostream>
using namespace std;
class B
{
public:
B(int x ) //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob =5;
ob=6;
ob=7;
return 0;
}
This produces the following output:
Constructor called
Constructor called
Constructor called
I fail to understand why is the constructor being called thrice with each assignment to object ob.
B ob =5;
This uses the given constructor.
ob=6;
This uses the given constructor because there is not a B& operator=(int) function and 6 must be converted to type B. One path to do this is to temporarily construct a B and use it in the assignment.
ob=7;
Same answer as above.
I fail to understand why is the constructor being called thrice with each assignment
As I stated above you do not have a B& operator=(int) function but the compiler is happy to provide a copy assignment operator (i.e., B& operator=(const B&);) for you automatically. The compiler generated assignment operator is being called and it takes a B type and all int types can be converted to a B type (via the constructor you provided).
Note: You can disable the implicit conversion by using explicit (i.e., explicit B(int x);) and I would recommend the use of explicit except when implicit conversions are desired.
Example
#include <iostream>
class B
{
public:
B(int x) { std::cout << "B ctor\n"; }
B(const B& b) { std::cout << B copy ctor\n"; }
};
B createB()
{
B b = 5;
return b;
}
int main()
{
B b = createB();
return 0;
}
Example Output
Note: Compiled using Visual Studio 2013 (Release)
B ctor
This shows the copy constructor was elided (i.e., the B instance in the createB function is triggered but no other constructors).
Each time you assign an instance of the variable ob of type B an integer value, you are basically constructing a new instance of B thus calling the constructor. Think about it, how else would the compiler know how to create an instance of B if not through the constructor taking an int as parameter?
If you overloaded the assignment operator for your class B taking an int, it would be called:
B& operator=(int rhs)
{
cout << "Assignment operator" << endl;
}
This would result in the first line: B ob = 5; to use the constructor, while the two following would use the assignment operator, see for yourself:
Constructor called
Assignment operator
Assignment operator
http://ideone.com/fAjoA4
If you do not want your constructor taking an int to be called upon assignment, you can declare it explicit like this:
explicit B(int x)
{
cout << "Constructor called" << endl;
}
This would cause a compiler error with your code, since it would no longer be allowed to implicitly construct an instance of B from an integer, instead it would have to be done explicitly, like this:
B ob(5);
On a side note, your constructor taking an int as parameter, is not a default constructor, a default constructor is a constructor which can be called with no arguments.
You are not taking the assignment operator into account. Since you have not defined your own operator=() implementation, the compiler generates a default operator=(const B&) implementation instead.
Thus, your code is effectively doing the following logic:
#include <iostream>
using namespace std;
class B
{
public:
B(int x) //custom constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
B& operator=(const B &b) //default assignment operator
{
return *this;
}
};
int main()
{
B ob(5);
ob.operator=(B(6));
ob.operator=(B(7));
return 0;
}
The compiler-generated operator=() operator expects a B object as input, but you are passing an int value instead. Since B has a non-explicit constructor that accepts an int as input, the compiler is free to perform an implicit conversion from int to B using a temporary object.
That is why you are seeing your constructor invoked three times - the two assignments are creating temporary B objects.

Copy constructors and Assignment Operators

I wrote the following program to test when the copy constructor is called and when the assignment operator is called:
#include
class Test
{
public:
Test() :
iItem (0)
{
std::cout << "This is the default ctor" << std::endl;
}
Test (const Test& t) :
iItem (t.iItem)
{
std::cout << "This is the copy ctor" << std::endl;
}
~Test()
{
std::cout << "This is the dtor" << std::endl;
}
const Test& operator=(const Test& t)
{
iItem = t.iItem;
std::cout << "This is the assignment operator" << std::endl;
return *this;
}
private:
int iItem;
};
int main()
{
{
Test t1;
Test t2 = t1;
}
{
Test t1;
Test t2 (t1);
}
{
Test t1;
Test t2;
t2 = t1;
}
}
This results in the following output (just added empy lines to make it more understandable):
doronw#DW01:~$ ./test
This is the default ctor
This is the copy ctor
This is the dtor
This is the dtor
This is the default ctor
This is the copy ctor
This is the dtor
This is the dtor
This is the default ctor
This is the default ctor
This is the assignment operator
This is the dtor
This is the dtor
The second and third set behave as expected, but in the first set the copy constructor is called even though the assignment operator is used.
Is this behaviour part of the C++ standard or just a clever compiler optimization (I am using gcc 4.4.1)
No assignment operator is used in the first test-case. It just uses the initialization form called "copy initialization". Copy initialization does not consider explicit constructors when initializing the object.
struct A {
A();
// explicit copy constructor
explicit A(A const&);
// explicit constructor
explicit A(int);
// non-explicit "converting" constructor
A(char const*c);
};
A a;
A b = a; // fail
A b1(a); // succeeds, "direct initialization"
A c = 1; // fail, no converting constructor found
A d(1); // succeeds
A e = "hello"; // succeeds, converting constructor used
Copy initialization is used in those cases that correspond to implicit conversions, where one does not explicitly kick off a conversion, as in function argument passing, and returning from a function.
C++ standard 8.5/12
The initialization that occurs in
argument passing, function return,
throwing an exception (15.1), handling
an exception (15.3), and
brace-enclosed initializer lists
(8.5.1) is called copy-initialization
and is equivalent to the form
T x = a;
The initialization that occurs in new
expressions (5.3.4), static_cast
expressions (5.2.9), functional
notation type conversions (5.2.3), and
base and member initializers (12.6.2)
is called direct-initialization and is
equivalent to the form
T x(a);
Your first set is according to the C++ standard, and not due to some optimization.
Section 12.8 ([class.copy]) of the C++ standard gives a similar example:
class X {
// ...
public:
X(int);
X(const X&, int = 1);
};
X a(1); // calls X(int);
X b(a, 0); // calls X(const X&, int);
X c = b; // calls X(const X&, int);
The last line would be the one matching your case.