initialization: parenthesis vs. equals sign
T a(b);
Calls a constructor of a
that accepts b
. (If b
is of the same type, then the copy constructor is called).
T a = b;
a temporary object of type T
is created to be constructed by b
. Then the copy constructor is called(=
is not an assignment in this case and the next case!).
T a = T(b);
Same as above! except that we explicitly constructed a temporary object.
Note that the standard allows total elimination of temporary copies in the second and third case. Also, if b
is not of type T
, then in the first case T
doesn't have to have a copy constructor. In the second and third cases, even though the implementation is free to optimize the whole thing, it still requires an accessible copy constructor. IIRC the standard calls this: copy elision.
T a( b );
is direct initialization, unless it parses as a function declaration, in which case it's a function declaration.
T a = b;
is copy initialization, which means that it works as if a temporary object is constructed on the right hand side, and that a
is then copy constructed or, in C++11 and later, possibly move constructed, from that temporary.
The compiler is free to elide (remove) the temporary+copying/moving whenever it can, but a copy or move constructor, whichever would be logically used, must still be accessible and not explicit
.
For example, in C++03 you cannot copy-initialize a std::ostringstream
, because it doesn't have a copy constructor. In C++11 you can copy-initialize an ostringstream
if the initializer is a temporary, which then results in a logical move construction (which however will usually be elided, optimized away). For example, this copy initialization declaration,
ostringstream s = ostringstream( "blah" );
… doesn't compile as C++03, because in C++03 the copy initialization invokes the class' copy constructor, which doesn't exist. It does however compile as C++11, because in C++11 the copy initialization invokes the move constructor. And while (to maintain its illusion of being a stream) a std::ostringstream
can't be directly copied, it can be moved.
Another such difference: in C++03 only the copy initialization syntax supports curly braces initializer, which in C++03 you can use when T
is an aggregate type such as a raw array. In C++11 the curly braces notation has been extended and generalized as a uniform initialization syntax, so it can be used also with direct initialization. And so the following direct initialization declaration,
int v[]{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 4 };
… does not compile as C++03, but does compile as C++11 and later.
The =
copy initialization syntax is the original initialization syntax from C.
And in C++11 and later, due to move semantics, it can be used in a much wider range of cases than in C++03, such as with a std::ostringstream
.