I am wondering that object construction with assignment operator works, never seen that before I saw this question:
return by value calls copy ctor instead of move
Reduced example code:
class A
{
public:
int x;
A(int _x):x(_x){ std::cout << "Init" << std::endl;}
void Print() { std::cout << x << std::endl; }
A& operator = ( const int ) = delete;
};
int main()
{
A a=9;
a.Print();
}
Is writing of
A a(9);
A a{9};
A a=9;
all the same?
This has nothing to do with assignment operator, it's initialization, more precisely copy initialization, which just uses the equals sign in the initializer.
when a named variable (automatic, static, or thread-local) of a non-reference type T is declared with the initializer consisting of an equals sign followed by an expression.
For A a = 9; the approriate constructor (i.e. A::A(int)) will be invoked to construct a. 1
A a(9); is direct initialization, A a{9}; is direct list initialization (since C++11), they all cause the A::A(int) to be invoked to construct the object for this case. 2
1 Before C++17 the appropriate move/copy constructor is still required conceptually. Even though it might be optimized out but still has to be accessible. Since C++17 this is not required again.
2 Note that there're still subtle differences among these initialization styles, they may lead to different effects in some specialized cases.
Related
I'm testing c++ class initialization.
class Point
{
private:
int x,y;
public:
Point() = delete;
Point(int a):x(a), y(0) { std::cout << "Conversion" << std::endl;}
Point(const Point&) { std::cout << "Copy constructor" << std::endl;}
//Point(const Point&) = delete;
Point& operator=(const Point&) = delete;
Point(Point&&) = delete;
Point& operator=(Point&&) = delete;
};
int main()
{
Point p1(5); //case1
Point p2 = 5; //case2
return 0;
}
In the above code, I thought "5" will be converted to a temp object by conversion constructor for both case1/2 at first. And then, I expected that copy constructor must be used for initializing of p1 & p2. But, it was not.
When I run this code, I saw just two "Conversion" message in console. No "Copy Constructor" message.
Even though, I deleted all copy constructor, move constructor, copy assignment operator and move assignment operator, this code worked well.
I would appreciate if you let me know what special member function will be used for initializing after creating temp object for "5",
I am using g++ compiler with std=c++17 option.
Case I
In case 1 the converting constructor is used since you have provided a constructor that can convert an int to Point. This is why this constructor is called converting constructor.
Case II
From mandatory copy elison
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type.
In case 2, even if you delete copy/move constructor only the converting constructor will be used. This is due to mandatory copy elison(in C++17) as quoted above.
Hi i am trying to understand how copy constructor works and looking at an example. The example is as follows:
{//new scope
Sales_data *p = new Sales_data;
auto p2 = make_shared<Saled_data>();
Sales_data item(*p); // copy constructor copies *p into item
vector<Sales_data> vec;
vec.push_back(*p2);// copies the object to which p2 points
delete p;
}
My question is :
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized, so why they have written copy constructor copies *p into item in the comment.
Now, to verify this for myself, i tried creating a simple example myself, but there also i am unable to understand the concept properly. My custom example is as follows:
#include<iostream>
#include<string>
class MAINCLASS{
private:
std::string name;
int age =0;
public:
MAINCLASS(){
std::cout<<"This is default initialization"<<std::endl;
}
MAINCLASS(MAINCLASS &obj){
std::cout<<"This is direct initialization"<<std::endl;
}
MAINCLASS(const MAINCLASS &obj):name(obj.name),age(obj.age){
std::cout<<"This is copy initialization"<<std::endl;
}
};
int main(){
MAINCLASS objectone;
MAINCLASS objecttwo =objectone;
MAINCLASS objectthree(objectone);
return 0;
}
Now when i run this program, i get the following output:
This is defalut initialization
This is direct initialization
This is direct initialization
My question from this program is as follws:
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;? I have read that in direct initialization function matching is used and in copy constructor , we copy the right hand operand members into left hand operand members. So when i write MAINCLASS objecttwo =objectone; it should call the copy constructor and print "this is copy initialization" on the screen. But instead it is direct initializing the object. What is happening here?
Despite the poor choice of name, copy initialization is orthogonal to copy constructors.
A copy constructor is any constructor whose first parameter is a lvalue reference to its class type, and can be called with just one argument. It's just a constructor that can initialize new objects from existing objects. That's pretty much all there is to it. Both the constructors you declared are in fact copy constructors. This one would be too
MAINCLASS(MAINCLASS volatile &obj, void *cookie = nullptr) {
// .. Do something
// This is a copy c'tor since this is valid:
// MAINCLASS volatile vo;
// MAINCLASS copy1_vo(vo);
}
And as the other answers noted copy initialization is simply the name for a family of initialization contexts. It includes initialization involving =, passing arguments to functions, return statements and throw expressions (and I'm probably forgetting something). Direct initialization involves other contexts.
A copy constructor can be used in any of the above. Be it copy initialization or direct initialization. The difference between the two - as appertains to constructors - is how an overload set of constructors is built. Copy initialization doesn't make use of constructors declared explicit. For instance, in this example
struct Example {
Example() = default;
explicit Example(Example const&) {}
};
int main() {
Example e;
Example e1(e); // Okay, direct initialization
Example e2 = e1; // Error! Copy initialization doesn't make use of explicit constructor
}
Even though we have a copy constructor, it can't be called in a copy-initialization context!
As far as the unexpected print out of your program, it's simply a matter of overload resolution choosing a more matching function. Your origin object is not declared const. So binding it to a non-const lvalue reference is simply the preferred choice in overload resolution.
Don't confuse copy construction and copy initialisation. You can copy-construct using direct or copy initialisation.
Copy initialisation refers to a set of initialisation syntax and semantics. This includes the T a = b syntax.
The copy constructor is a special class method that takes an argument of said class. This method should only take one parameter (both T& or const T& will do). Copy construction occurs when that function is called.
With this in mind, we can go on to answer your questions.
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized...
Both Sales_data item = *p and Sales_data item(*p) call the copy constructor. But, the former uses copy initialisation (T a = b), whereas the latter uses direct initialisation (T a(b)).
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;?
Actually, the issue here isn't whether it's copy/direct initialised. This is an issue of lvalue/rvalue overload resolution.
Consider the following program:
#include <iostream>
void f(int& i) { std::cout << "int&\n"; }
void f(const int& i) { std::cout << "const int&\n"; }
int main() {
f(1); // f(const int&)
int i = 2;
f(i); // f(int&)
}
f is chosen based on whether the value passed is lvalue or rvalue. In the first case, 1 is an rvalue, so f(const int&) is called (see this). In the second case, i is an lvalue, and f(int&) is chosen since it's more general.
So in your case, both MAINCLASS objecttwo =objectone; and MAINCLASS objectthree(objectone); call the copy constructor. And again, the former uses copy initialisation, whereas the latter uses direct initialisation. It's just that both of these calls choose the non-const ref overload instead: MAINCLASS(MAINCLASS&).
Copy initialization and direct initialization is based on the syntax used to construct.
See Confusion in copy initialization and direct initialization.
Which constructor gets invoked is based on overload resolution (and not the syntax to construct)
The compiler invokes the function which best matches the passed arguments to the defined parameters.
In your example since objectone is non-const, the best match is the copy constructor with a non-const parameter. Since the other copy constructor has a const& parameter, it will get invoked for a const object.
Rewriting your example:
#include<iostream>
#include<string>
class MAINCLASS {
private:
std::string name;
int age = 0;
public:
MAINCLASS() {
std::cout << "This is default initialization" << std::endl;
}
MAINCLASS(MAINCLASS& obj) {
std::cout << "This is copy constructor with non-const reference parameter" << std::endl;
}
MAINCLASS(const MAINCLASS& obj) :name(obj.name), age(obj.age) {
std::cout << "This is copy constructor with const reference parameter" << std::endl;
}
};
int main() {
MAINCLASS objectone;
const MAINCLASS const_objectone;
MAINCLASS objecttwo = objectone; // copy initialization of non-const object
MAINCLASS objectthree(objectone); // direct initialization of non-const object
MAINCLASS objectfour = const_objectone; // copy initialization of const object
MAINCLASS objectfive(const_objectone); // direct initialization of const object
return 0;
}
The output would be:
This is default initialization
This is default initialization
This is copy constructor with non-const reference parameter
This is copy constructor with non-const reference parameter
This is copy constructor with const reference parameter
This is copy constructor with const reference parameter
cppreference says:
The underlying array is a temporary array of type const T[N], in which
each element is copy-initialized (except that narrowing
conversions are invalid) from the corresponding element of the
original initializer list. The lifetime of the underlying array is the
same as any other temporary object, except that initializing an
initializer_list object from the array extends the lifetime of the
array exactly like binding a reference to a temporary (with the same
exceptions, such as for initializing a non-static class member). The
underlying array may be allocated in read-only memory.
What's the reasoning behind this decision? Why is moving not ok?
What about copy-ellision?
struct A { A(const A&){ std::cout << "Oh no, a copy!\n"; } };
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ A{} };
return 0;
}
My compiler ellides the copy. But are these copies guaranteed to be ellided?
"Copy initialization" in C++ doesn't mean things will necessarily be copied. It's just a formal name for the constraints under which initialization will occur. For instance, when copy initializing, explicit c'tors are not candidates. So the following code will be ill-formed
#include <iostream>
#include <initializer_list>
struct A {
explicit A() = default;
A(const A&){ std::cout << "Oh no, a copy!\n"; }
};
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ {} };
return 0;
}
The single member in the list needs to be copy-initialized from {}, which entails calling the default c'tor. However, since the c'tor is marked explicit, this initialization cannot happen.
Copy elision is certainly possible pre-C++17, and is mandatory in C++17 onward in certain contexts. In your example, under a C++17 compiler, since you provide an initializer that is a prvalue (a pure rvalue, not an object), the initialization rules of C++ mandate that the target is initialized directly, without intermediate objects created. Even though the context is called "copy initialization", there are no superfluous objects.
I don't understand the difference between the two following statements:
Thing thing;
Thing thing = Thing();
Both create a Thing object and put it in the variable thing, right? If so, two questions:
1- What are the technical differences between the two?
2- When should I use one over the other?
Please note:
A- I am not using C++ 11.
B- New to C++, please use newbie-friendly words.
Thing thing;
is default-initialization. If Thing is a class type, it calls the default constructor of Thing, and that's it.
Thing thing = Thing();
value-initializes a temporary Thing and then copies/moves that temporary into thing. In practice compilers will elide the copy/move, making it effectively a value-initialization, but this still requires a copy/move constructor to be available. The = Thing(); syntax is needed to get value-initialization semantics because Thing thing(); is the vexing parse.*
The difference between value-initialization and default-initialization is that in some cases (the exact cases depend on the version of the standard, but a non-union class type with no user-provided constructors, and non-class non-array types, along with arrays of these types qualify in all versions), value-initialization will zero-initialize first before calling the default constructor.
Using the second version is helpful if Thing can be a non-class (e.g., in a template) or is a class type that would get the zero-initialization treatment (e.g., a POD class) and you want it to have well-defined values.
*Thing thing{}; is subtly different in initialization semantics from plain value-initialization in C++14.
The notation
Thing thing = Thing();
which does a copy initialization, could be used in C++03 template code to effectively default-construct a Thing. The thing is that this works also for built-in types Thing such as int. This contorted notation because writing Thing thing(); just declares a function (a special case of “the most vexing parse”).
In C++11 one can instead write
Thing thing{};
An alternative technique in C++03 was to wrap Thing in a struct, like this:
struct Initialized_thing
{
Thing thing;
Initialized_thing(): thing() {}
};
The difference is that the compiler can issue an error for the second definition if the copy or move constructor is not accessible. For example they might be defined as private or deleted.
Thing thing;
Thing thing = Thing(); // there may be a compiler error
For example consider the following programs
int main()
{
class A
{
private:
A( const A & );
public:
A() {}
};
A a;
return 0;
}
The program above will be compiled successfuly while the program below will not be compiled (if only the compiler is not for example MS VC++ 2010)
int main()
{
class A
{
private:
A( const A & );
public:
A() {}
};
A a = A();
return 0;
}
The other difference that in this statement
A a;
a is default initialized
while in this statement
A a = A();
a is value initialized.
From the C++ Standard
2 The expression T(), where T is a simple-type-specifier or
typename-specifier for a non-array complete object type or the
(possibly cv-qualified) void type, creates a prvalue of the specified
type, whose value is that produced by value-initializing (8.5) an
object of type T; no initialization is done for the void() case.
Consider another example
int main()
{
class A
{
int x;
};
A a1;
A a2 = A();
return 0;
}
For a1 value of x will be unspecified while for s2 value of x will be equal to 0.
As others have written the syntax
Thing obj;
will default-initialize your object, while the syntax
Thing obj = Thing();
will value-initialize your object.
The second syntax causes more stuff to be involved:
A move/copy constructor should be accessible
A temporary is value-initialized
Copy elision takes place
They usually serve different purposes and although the first one is the most commonly used to initialize an object, the latter is different in the value-initialization meaning.
I recommend reading the documentation for the differences between value and default initialization, anyway a simple example to show the differences is the following:
class Thing {
public:
int data;
Thing() {
cout << "default ctor" << endl;
}
Thing(const Thing&) {
cout << "copy ctor" << endl;
}
const Thing& operator=(const Thing&) {
cout << "operator=" << endl;
return *this;
}
};
struct Thingy {
int data;
};
int main() {
cout << is_pod<Thing>::value << endl; // Thing is NOT pod
Thing thing; // Calls default constructor
cout << thing.data << endl; // Garbage
Thing thing2 = Thing(); // Copy elision is able to optimize this (standard-defined 12.8/31). Anyway a copy/move constructor must be accessible
cout << thing2.data << endl; // Garbage
// -----------------------------------------------------
cout << is_pod<Thingy>::value << endl; // Thingy IS pod
Thingy thingy1;
cout << thingy1.data << endl; // garbage
Thingy thingy2 = Thingy();
cout << thingy2.data << endl; // zero-initialized!
}
The above code will print
0 // Not a POD
default ctor
-1326900592 // garbage
default ctor
-1326900592 // garbage
// ---------------------------
1 // POD
-1326900592 // garbage
0 // zero-initialized
This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 1 year ago.
Simple question: are the following statements equivalent? or is the second one doing more implicit things behind the scenes (if so, what?)
myClass x(3);
myClass x = myClass(3);
Thanks!
They are not completely identical. The first is called "direct initialization" while the second is called "copy initialization".
Now, the Standard makes up two rules. The first is for direct initialization and for copy initialization where the initializer is of the type of the initialized object. The second rule is for copy initialization in other cases.
So, from that point of view both are termed in one - the first - rule. In the case where you have copy initialization with the same type, the compiler is allowed to elide a copy, so it can construct the temporary you create directly into the initialized object. So you can end up very well with the same code generated. But the copy constructor, even if the copy is elided (optimized out), must still be available. I.e if you have a private copy constructor, that code is invalid if the code in which it appears has no access to it.
The second is called copy-initialization, because if the type of the initializer is of a different type, a temporary object is created in trying to implicitly convert the right side to the left side:
myclass c = 3;
The compiler creates a temporary object of the type of myclass then when there is a constructor that takes an int. Then it initializes the object with that temporary. Also in this case, the temporary created can be created directly in the initialized object. You can follow these steps by printing messages in constructors / destructors of your class and using the option -fno-elide-constructors for GCC. It does not try to elide copies then.
On a side-note, that code above has nothing to do with an assignment operator. In both cases, what happens is an initialization.
The second one may or may not call for an extra myclass object construction if copy elision is not implemented by your compiler. However, most constructors, have copy elision turned on by default even without any optimization switch.
Note initialization while construction never ever calls the assignment operator.
Always, keep in mind:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
In the second one, a temporary object is created first and then is copied into the object x using myClass's copy constructor. Hence both are not the same.
I wrote the following to try and illustrate understand what's going on:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
When I compile and run this code I get the following output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
This would seem to indicate that there is no difference between the two calls made in the main function, but that would be wrong. As litb pointed out, the copy constructor must be available for this code to work, even though it gets elided in this case. To prove that, just move the copy constructor in the code above to the private section of the class definition. You should see the following error:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Also note that the assignment operator is never called.