Is it direct-initialization or copy-initialization

2019-02-20 01:08发布

问题:

Initializing objects (instances of classes or structs) in C++ can be done in various ways. Some syntaxes evoke a direct-initialization of your object, other syntaxes lead to a copy-initialization. With copy-elision enabled in the compiler, both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when you choose for the latter (copy-initialization).

Conclusion: copy-initialization can have a performance-penalty!

From the following question: C++11 member initializer list vs in-class initializer? I can conclude that this would be copy-initialization syntax:

obj s = obj("value");

And this would be direct-initialization syntax:

obj s{"value"};

 
But what about this one:

obj s = {"value"};

And this one:

obj s = obj{"value"};

And this one:

obj s("value");

Or this one:

obj s = "value";

NOTE
Bjarne Stroustrup compares a few initialization styles (but not all) in his book "Programming, Principles and Practice Using C++" 2nd edition, on page 311, §9.4.2:

struct Date {
    int y,m,d;                     //year, month, day
    Date(int y, int m, int d);     //check for valid date and initialize
    void add_day(int n);           //increase the Date by n days
};

...

Date my_birthday;                   //error: my_birthday not initialized
Date today{12,24,2007};             //oops! run-time error
Date last{2000,12,31};              //OK (colloquial style)
Date next = {2014,2,14};            //also OK (slightly verbose)
Date christmas = Date{1976,12,24};  //also OK (verbose style)

Mr. Stroustrup presents these different initialization styles as equal. At least, that's how it looks to me. Nevertheless, it could still be possible that some are direct-initialization and others copy-initialization, since those terms are not yet discussed at that point in the book.


EDIT
The given answers bring up something interesting.
Apparently, this is direct-initialization:

obj s("value");

And this is direct-list-initialization:

obj s{"value"};

As some of you point out, there is a difference. In what way do they actually differ? Would the difference be noticeable in the output of a non-optimizing compiler?

回答1:

obj s = obj("value");

This is direct initialization of a prvalue, which is then used to copy initialize the variable s. C++17's prvalue rules make this de-facto direct initialization of s.

obj s{"value"};

This is direct-list-initialization. The "list" part is important. Anytime you apply a braced-init-list for the purposes of initializing an object, you are performing list-initialization.

obj s = {"value"};

This is copy-list-initialization.

obj s = obj{"value"};

This is direct-list-initialization of a prvalue, which is then used to copy initialize the variable s.

obj s("value");

That is direct initialization.

obj s = "value";

That's copy initialization.

Mr. Stroustrup presents these different initialization styles as equal.

They are equal in the sense that they do mostly the same thing. But they're not technically equal; copy-list-initialization cannot call explicit constructors. So if the selected constructor were explicit, the code would fail to compile in the copy-list-initialization cases.



回答2:

In general:

  • T s(...); or T s{...}; is direct-initialization
  • T s = ...; is copy-initialization1

In copy-initialization, the right-hand side is implicitly converted to a temporary instance of the type T, from which s is subsequently copy/move-constructed.

Mr. Stroustrup presents these different initialization styles as equal.

In many cases the generated (optimized) code is indeed exactly the same. Compilers are allowed to elide the copy construction (even if it has side effects). Modern compilers are well beyond simple optimizations such as this one so you can effectively count on this elision (which is required in C++17).

The difference between copy and direct initialization is nevertheless very important because the semantics are different; for example, invoking constructors declared explicit is only possible in direct-initialization.


1 The form T s = {...}; is copy-list-initialization and follows some special list-initialization rules.



回答3:

You can easily look up the answers to these sorts of questions. That said, the simple answer is that an = means copy-initialization. However, T t={...}; is copy-list-initialization, which (unless the braces contain only a T or something derived from it) does not involve a copy! It does, however, disallow the use of non-explicit constructors.