What is the difference between MyClass x(1,2,3);
and MyClass x = MyClass(1,2,3);
in c++?
If MyClass(1,2,3)
works on its own, then can I write:
doSomething(MyClass(1,2,3));
instead of
MyClass x(1,2,3);
doSomething(x);
What is the difference between MyClass x(1,2,3);
and MyClass x = MyClass(1,2,3);
in c++?
If MyClass(1,2,3)
works on its own, then can I write:
doSomething(MyClass(1,2,3));
instead of
MyClass x(1,2,3);
doSomething(x);
Due to mandatory copy elison from C++17, both(MyClass x(1,2,3)
and MyClass x = MyClass(1,2,3)
)of them are the same.
This can be seen from the following quoted statement:
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:
This means that in MyClass x = MyClass(1,2,3);
there can be no call to the copy/move constructor and the object x
will be created as if you wrote MyClass x(1,2,3);
.
Prior to C++17, the second MyClass x = MyClass(1,2,3);
creates a temporary object and then copy/moves that temporary using the copy/move constructor. Note that prior to C++17, the compiler has the option to elide this copy/move operation.
Now what happens when doSomething
is called depends on the parameter type of that function. Below we consider different parameter types:
Suppose we have:
void doSomething(MyClass m)
{
}
doSomething(MyClass(1,2,3)); //no call to copy/move ctor can happen from c++17
Here from C++17, when passing MyClass(1,2,3)
there can be no call to the copy/move constructor due to mandatory copy elison. But prior to C++17, there can be a call to the copy/move constructor. There is also a possibility that copy/move operation is elided.
Suppose doSomething
is as follows:
//----------------------------v----->note the lvalue reference
void doSomething(const MyClass& m)
{
std::cout<<"doSomething"<<std::endl;
}
doSomething(MyClass(1,2,3));
Here there will be temporary materialization and m
will refer(bound) to that. Note there will be no call to copy/move constructor in this case in both C++11 and C++17.
Suppose doSomething
is as follows:
//----------------------vv---------->note &&
void doSomething(MyClass&& m)
{
std::cout<<"doSomething"<<std::endl;
}
doSomething(MyClass(1,2,3));
Here also there will be temporary materialization and m
will refer(bound) to that. Note there will be no call to copy/move constructor in this case in both C++11 and C++17.
Now we will consider what happens if you pass x
to doSomething
for different parameter type of doSomething
.
Suppose we have:
void doSomething(MyClass m)
{
}
MyClass x(1,2,3);
doSomething(x); //calls the copy constructor in both c++17 and prior to c++17
Here we're passing x
by value to the function doSomething
and this will be done using the copy constructor of class MyClass
as it involves copy initialization in both C++17 and prior to C++17.
Suppose we have:
//----------------------------v------>note lvalue reference
void doSomething(const MyClass& m)
{
}
MyClass x(1,2,3);
doSomething(x); //the parameter `m` will be bound to the passed argument x in both c++17 and prior to c++17
Here the parameter m
will be bound to the passed argument x in both c++17 and prior to c++17. No call to the copy constructor will be made.
Suppose we have:
//----------------------vv-------->note &&
void doSomething(MyClass&& m)
{
}
MyClass x(1,2,3);
doSomething(x); //WON'T COMPILE as we can't bind an lvalue to an rvalue reference