简体   繁体   中英

Is there any subtle difference between prepending '=' to initializer lists?

Is there any subtle difference between these two ways of initializing variables in C++11?

  1. vector<double> v { 0.0, 1.1, 2.2, 3.3 };

  2. vector<double> v = { 0.0, 1.1, 2.2, 3.3 };

Can the latter be used for all the same cases as the first one?

Stroustrup claims in TCPL4ED that the first way is the only one that can be used in every context, and thus recommends it. Later on, he seems to imply that the second one is just a different way of writing the first one.

1.

vector<double> v { 0.0, 1.1, 2.2, 3.3 };

Is a direct-list-initialization . It means that it is initialized with a constructor taking an initializer list.

Constructor :

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );

2.

vector<double> v = { 0.0, 1.1, 2.2, 3.3 };

Is a copy-list-initialization .


The standard is pretty clear :

8.5.4 List-initialization [dcl.init.list]

List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copyinitialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization. [ Note: List-initialization can be used :

  • as the initializer in a variable definition

[...]

Example :

  std::complex<double> z{1,2}; [...] std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} }; 

For the difference between both, we should go a little bit further :

13.3.1.7 Initialization by list-initialization [over.match.list]

  • For direct-list-initialization , the candidate functions are all the constructors of the class T.
  • For copy-list-initialization , the candidate functions are all the constructors of T. However, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This restriction only applies if this initialization is part of the final result of overload resolution — end note ]

The purpose of uniform initialization was (in part) to remove the difference between these two constructs. To ensure that they would have the same functionality.

Sadly, they failed. There is exactly one difference between direct-list-initialization (ie: T t{...} ) and copy-list-initialization (ie: T t = {...} ). You don't need a copy/move constructor for copy-list-initialization (despite the name); section 8.5.4 doesn't list that as a requirement. There's no notion of a temporary being constructed that might be elided away. They have identical behavior except :

Copy-list-initialization will fail if it selects an explicit constructor. That's the only difference.

It's hard to give a quote from the spec, because there are only 3 mentions of what copy-list-initialization is. One is in 8.5.4, where it defines that copy-list-initialization is a form of list-initialization, another stating that returning a braced-init-list uses copy-list-initialization. And the last in 13.3.1.7, where it states the above exception:

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

So that's the only difference.

One difference which comes to mind is that the first form can be used even if the constructor is declared explicit while the second cannot. Sorry but, @Timothy Shields and @Tomek are wrong, both statements are direct initializations, not copy initialization.

I guess this is similar to the case with normal constructors (ie not using initializer lists). The first choice is a construction using (in this case) constructor taking initializer list constructor. The other choice is a temporary created with initializer list constructor and then the variable is copy-constructed from that. Temporary then goes away. Note that the latter usually skips temporary and copy destruction as (N)RVO kicks in BUT the latter requires that your class has accessible copy constructor at the point of definition. At least this is what I remember from C++03, it MAY have changed in C++11.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM