Peter said:
I always thought that
type var = initial;
Copy-initialization.
is the same as
type var(initial);
Direct-initialization.
No, they are not the same. They are _almost_ the same in only one case -
when 'initial' has the same type as 'var'. Otherwise, these are not even
remotely the same (at least conceptually, in general case).
Even when the types are the same, declaring the copy-constructor as
'explicit' will break the former variant.
And the difference is only syntactic sugar, plus the latter may have
ambiguitiy problems as it looks like a function declaration.
The latter (as well as the former) can have other problems, including
ambiguity problems.
The former calls a constructor of "type" that receives a parameter of
"initial"'s type,
as does the latter. Provided a constructor with a
matching parameter type exists for "type".
That's incorrect. A counterexample that illustrates the difference is
rather easy to produce
struct S { operator int() const; };
struct D { D(int); };
S s;
D d1(s); // OK
D d2 = s; // ERROR
In this case the the 'd1' is initialized with a constructor that
receives a completely different type (not 'S'). And the initialization
of 'd2' does not compile at all.
Everything else would make no sense, as the former syntax is clearly
construction with initialization (i.e. the same as the latter),
Quite the opposite. The syntax is clearly different, which provides the
opportunity to assign a different semantics to that syntax, which is
what was done in this case.
For the OP's examples,
The copy constructor, no doubt. The compiler-generated one, if not defined.
No. There's no way to tell what's happening in this case without knowing
the type of 'obj2'. Even if 'obj2' has the same type as 'obj1', it still
doesn't mean that the copy constructor is used.
A counterexample is easy to produce
struct D {
explicit D(const D&);
D(int);
operator int() const;
};
D d1(0);
D d2 = d1;
The copy-constructor is not used in this case.
The constructor that takes a "const char *", if provided. And it must be
provided.
Correct, it must be provided for the purpose of creating the
aforementioned temporary.
Even if the compiler wanted to first construct a temporary and
use that to to copy construct str, the temporary itself couldn't be
constructed without a constructor that takes a "const char *".
Precisely. But this is only because the RHS is a string literal, not an
object of class type. If the RHS was a class, there would be an
alternative path for creating the temporary - the conversion operator
struct D {};
struct S { operator D() const; };
S s;
D d = s;
In this case class 'D' doesn't have a conversion constructor from 'S',
yet the code is well-formed.
I see no
reason why a compiler would ever choose to go the way of the temporary.
The reason is the requirement of the C++ language specification. The
compiler is required is use this approach as the "baseline" and then it
is allowed to "optimize away" the temporary. Nevertheless, the
well-formed-ness of the code is verifies as if the temporary is created
in any case.
The same applies to the explicit constructor call syntax. The compiler
could also generate a temporary here to convert from "const char *" to
"string",
Incorrect. Target class has a constructor that accepts 'const char*',
which immediately means that it is explicitly prohibited from generating
an intermediate temporary of 'string' type by the language specification.
but it most likely does not.
In simply "does not". No "most likely" is involved here.
Even if the language specs allow use of a temporary (I don't know), what
sense would it make?
The "sense" in this case is not the temporary itself, but a specific
construction sequence (or "path") in case of copy-initialization, which
is required to involve the copy-constructor (at least conceptually). A
temporary is just a [undesired] side-effect of that specific
construction paths, which is why the language specification explicitly
allows it to be "optimized away".
Once again, to illustrate the difference between these construction
paths, I already provided you with one example (here it is again)
struct D { D(int); };
struct S {
operator int() const;
};
S s;
D d1(s); // OK
D d2 = s; // ERROR
But once we make the following modification, we get an opposite effect
struct D { D(int); };
struct S {
operator int() const;
operator D() const; // <- added
};
S s;
D d1(s); // ERROR
D d2 = s; // OK
Consider these two examples carefully and they will help you to
understand the difference between direct-initialization and
copy-initialization.