Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Copy and Direct Initialization

49 views
Skip to first unread message

Alf P. Steinbach

unread,
Aug 22, 2015, 10:15:38 AM8/22/15
to
On 22.08.2015 14:18, Stefan Ram wrote:
> I would like to understand the difference between copy and
> direct initialization, that is, between T x = a; versus
> T x( a ); / T x{ a }.
>
> 1st: With direct initialization available, is there a
> reason anyone would ever want to use copy initialization
> when writing new code? Are there use cases where copy
> initialization is much better than direct initialization?

"=" was the original C initialization syntax.

I find it more clear and easy to recognize when I see that "=".

With C++03 there were some good reasons to use direct initialization
here and there, in particular for non-copyable types like streams, but
in C++11 and later they're typically movable.


> 2nd: On a webpage, someone said that direct initialization
> is like a constructor call, possibly with conversions of
> the arguments used,

No, the word "like" indicates some difference in the details, but it
just is a constructor call, plain and simple.


> while copy initialization will
> »construct a conversion sequence«. Does the word »sequence«
> hint at a possible fact that direct initialization is
> using /at most one/ conversion per argument while copy
> initialization might use /a sequence of/ several conversions
> to convert the right-hand side to the type of the left-hand side?

The sequence for copy initialization of a T object is (1) conversion to
T, and (2) a copy or move constructor call of T. Where the latter can be
optimized away, regardless of side-effects of those constructors (they
must still be accessible though, so that the code can compile with a
compiler that doesn't do that optimization). IIRC correctly the standard
uses the word "elided", but that may be just a word we used here in the
Usenet groups.


> 3rd: IIRC, direct initialization can implicitly use
> type conversions that where marked with »explicit«, while
> copy initialization does not. So this might be a use case
> for copy initialization: When one does not want to use
> explicit conversions?

Yes, but I have not encountered any case where I'd want that.


> Are there other important differences
> between direct initialization and copy initialization that
> can decide which one to choose for reasons of good style
> or secure or efficient coding in cases where both would be
> possible?

Yes, even though one might argue that that's already covered by the
clarity & easy-to-recognize reason, one might use copy initialization to
avoid verbosity.

long_silly_type_spec o1( foo<another_long_silly_type_spec>() );

auto o2 = foo<another_long_silly_type_spec>();

Or like

wchar_t const* const s1( L"Bah!" );

auto const s2 = L"Bah!";

Of course, there are also cases where it is direct initialization that
cuts down the verbosity.


Cheers & hth.,

- Alf

Victor Bazarov

unread,
Aug 22, 2015, 2:19:36 PM8/22/15
to
On 8/22/2015 8:18 AM, Stefan Ram wrote:
> I would like to understand the difference between copy and
> direct initialization, that is, between T x = a; versus
> T x( a ); / T x{ a }.
>
> 1st: With direct initialization available, is there a
> reason anyone would ever want to use copy initialization
> when writing new code? Are there use cases where copy
> initialization is much better than direct initialization?

No, but there are cases you have no control over. For instance,
function arguments are sometimes copy-initialized. Template code can be
written T x = a;, and it will only be instantiated if you use it.

> 2nd: On a webpage, someone said that direct initialization
> is like a constructor call, possibly with conversions of
> the arguments used, while copy initialization will
> »construct a conversion sequence«. Does the word »sequence«
> hint at a possible fact that direct initialization is
> using /at most one/ conversion per argument while copy
> initialization might use /a sequence of/ several conversions
> to convert the right-hand side to the type of the left-hand side?

The Standard says that there can be at most three conversions, IIRC. A
standard conversion followed by user-defined one, followed by another
standard conversion, but I don't remember all circumstances where those
apply.

> 3rd: IIRC, direct initialization can implicitly use
> type conversions that where marked with »explicit«, while
> copy initialization does not. So this might be a use case
> for copy initialization: When one does not want to use
> explicit conversions? Are there other important differences
> between direct initialization and copy initialization that
> can decide which one to choose for reasons of good style
> or secure or efficient coding in cases where both would be
> possible?

I am not sure it matters, really. However, I have seen code in which
copy-construction was simply disabled (or made inaccessible), and it
needs to be accessible in the scope in which

T x = a;

is used instead of

T x{a};

V
--
I do not respond to top-posted replies, please don't ask

Alf P. Steinbach

unread,
Aug 23, 2015, 8:57:12 AM8/23/15
to
On 22.08.2015 20:43, Stefan Ram wrote:
> [snip]
>
> #include <iostream>
>
> struct alpha { alpha(){ ::std::cout << "alpha\n"; }};
> struct beta { beta( alpha ){ ::std::cout << "beta( alpha )\n"; }};
> struct gamma { gamma( beta ){ ::std::cout << "gamma( beta )\n"; }};
>
> int main()
> {
> { beta b = alpha{}; /*
> gamma g = alpha{}; */ }

There is no direct way to construct a right hand side gamma from an
alpha, because that would involve a user defined conversion. The
compiler doesn't check such, it merely checks whether the actual
argument matches the formal argument type of some gamma constructor. It
/would/ match if alpha were derived from beta, but that's not a
conversion, that's an IS-A relationship.


> { beta b{ alpha{} };
> gamma g{ alpha{} }; }}

Here possible conversions to the formal argument type, are considered,
just as with any function call.


> alpha
> beta( alpha )
>
> alpha
> beta( alpha )
>
> alpha
> beta( alpha )
> gamma( beta )
>
> That is, the compiler used did /not/ use the sequence
> of two conversions in the case of /copy/ initialization.
> (This has been commented out above, because it gives an
> error message.)
>
> But it /did/ use the sequence in the case of /direct/
> initialization.

Well, there's only a single IMPLICIT conversion, namely from actual
argument type alpha to formal argument type beta. But the expression

gamma( alpha() )

is a conversion from alpha to gamma by way of beta. It involves two user
defined conversions, but crucially, only a single implicit one.

Here is a similar program that I think better illustrates how the limit
on number of implicit user defined conversions can be reached, and then
affects whether the code compiles or not:

#include <iostream>
using namespace std;

struct alpha { alpha() { cout << "alpha()" << endl; } };
struct beta { beta() { cout << "beta()" << endl; } };

struct gamma
{
operator alpha() { cout << "gamma -> alpha" << endl; return
alpha(); }
gamma( beta ){ cout << "gamma( beta )" << endl; }
gamma( alpha ) { cout << "gamma( alpha )" << endl; }
gamma( gamma& ) { cout << "gamma( gamma& )" << endl; }
};

int main()
{
//gamma g = beta(); // Over the limit.
gamma g = gamma( beta() ); // OK, invoking `operator alpha()`
}

Output:

beta()
gamma( beta )
gamma -> alpha
alpha()
gamma( alpha )

This was the trick employed by old `std::auto_ptr`. Or to be precise,
the trick that got standardized. It evolved a bit, as I recall.

A. Bolmarcich

unread,
Aug 25, 2015, 2:51:32 PM8/25/15
to
On 2015-08-22, Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> I would like to understand the difference between copy and
> direct initialization, that is, between T x = a; versus
> T x( a ); / T x{ a }.
>
> 1st: With direct initialization available, is there a
> reason anyone would ever want to use copy initialization
> when writing new code? Are there use cases where copy
> initialization is much better than direct initialization?

In "A Tour of C++", Bjarne Stroustrup gave the following advice: "Prefer
the = syntax for the initialization in declarations using auto."
0 new messages