Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference, if any, between {x} and '= {x}' initialization?

Tags:

c++

c++11

I am trying to understand the semantic difference between these two ways of initialization:

Foo foo{x};
Foo foo = {x};

I am interested to know the difference in the following cases:

  1. x is of type Foo.
  2. Foo has a constructor that takes an argument of the same type as that of x.
  3. x is not of type Foo but a converting constructor is available.
  4. x is not of type Foo but an explicit converting constructor is available.

By difference I mean, in each case:

  1. Conceptually, which constructors are invoked?
  2. Which constructors calls are usually optimized away by the compiler?
  3. Is implicit conversion allowed?
like image 421
Mustafa Avatar asked Feb 17 '15 18:02

Mustafa


1 Answers

Foo foo{x};    // 1

Foo foo = {x}; // 2

1 is direct-list-initialization. 2 is copy-list-initialization.

Assuming that Foo is a class type, then in most cases they do exactly the same thing, conceptually or otherwise, except that if an explicit constructor is selected during overload resolution, then #2 is ill-formed. In particular, unlike copy-initialization, copy-list-initialization does not conceptually construct a temporary.

The one exception is when x is of type Foo or a type derived therefrom. In this case, #1 is equivalent to Foo foo(x); (i.e., direct-initialization) and #2 is equivalent to Foo foo = x; (i.e., copy-initialization). The subtle difference is that overload resolution for #2 in this case considers only non-explicit constructors, rather than considering all constructors and then becoming ill-formed if an explicit constructor is selected.* This exception is added by the resolution of CWG issue 1467, which was adopted last November.


* You'd have to write some pretty tortured code for this to matter. For example:

struct X
{
    X() = default;
    explicit X(X&);
    X(const X&);
};

int main() { 
    X x1;
    X x2 = {x1}; // #1
    X x3 = x1;   // #2
}

Pre-CWG1467, line #1 is ill-formed because overload resolution selects X(X&), which is explicit. Post-CWG1467, the explicit constructor X(X&) isn't considered, so X(const X&) is used. Note that line #2 is always well-formed and uses X(const X&).

like image 54
T.C. Avatar answered Nov 20 '22 10:11

T.C.