简体   繁体   中英

Why does the initializer_list does not violate the rule of “One Class-Type Conversion”

The "One Class-Type Conversion" Rule is quoted from c++ Primer 5th § 7.5.4

Only One Class-Type Conversion Is Allowed
In § 4.11.2 (p. 162) we noted that the compiler will automatically apply only one class-type conversion. For example, the following code is in error because it implicitly uses two conversions:

     //item.combine() need a parameter of class Sales_data,which has a constructor of string type;
     item.combine("9-999-99999-9");
    // error: requires two user-defined conversions:
    // (1) convert "9-999-99999-9" to string
    // (2) convert that (temporary) string to Sales_data

The code above cause error because it violate the rule of "One Class-Type Conversion"; And here is an introduction of Implicit conversions
Based on the rule above,I came across 2 questions in studying c++,both of them are copy initialization ,which means that the copy constructor will be called .

example of string

Considering the code snippet string a="hello"; ,if we does not think about the fact that the compiler will often do some optimization like copy elision , then the code is interpreted like :

  1. convert the type of "hello" from char[6] to char* ; (convert 1)
  2. call the constructor of string(char*) and generate a temporary string object temp (convert 2)
  3. call the copy constructor of string(string&&) ,and finally get a

Question: OK,actually 2 convertion happens,why does that not violate the "One Class-Type Conversion" Rule ? Is it because the conversion from char[6] to char* is not a class-type conversion,since the char* is not class-type but a built-in type ?

example of initializer_list

once we use a list initialization of vector like

 vector<int> example={1,2,3,4,5}; 

then the code is interpreted like :

  1. {1,2,3,4,5} does not have a type like "hello",so there is no conversion like char[6] to char*
  2. convert {1,2,3,4,5} to temporary object of initializer_list<int> temp ( convert 1 )
  3. convert initializer_list<int> temp to vector<int> temp1 by using:( convert 2 )

     vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() ); 
  4. call the copy constructor of vector( vector&& other ); ,and finally we get vector<int> example ;

Question: Ooop!There are also 2 conversion happening! And there are all conversions of class-type ! What happened?

OK,since an hour passed and nobody here has answered this question,i've found the answer myself.

example of string

The reson is that char[5] to char* is not a class-type conversion,so it does not violate the One Class-Type Conversion rule.

Order of the conversions
Implicit conversion sequence consists of the following, in this order:
1) zero or one standard conversion sequence;
2) zero or one user-defined conversion;
3) zero or one standard conversion sequence.
When considering the argument to a constructor or to a user-defined conversion function, only one standard conversion sequence is allowed (otherwise user-defined conversions could be effectively chained). When converting from one built-in type to another built-in type, only one standard conversion sequence is allowed.

example of initializer_list

initializer_list is a special case,which is actually copy-list-initializaition but not copy-initialization ,so there are no 2 type conversion here . See list initialization

  • Otherwise, the constructors of T are considered, in two phases:
    All constructors that take std::initializer_list as the only argument , or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

So when we use a initializer_list,there is no conversion from other to T ,which is a requirement for copy-constructor . See Copy initialization

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion , which is a prvalue temporary (until C++17)prvalue expression (since C++17) if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)

So the constructor with parameter initializer_list<int> will be directly called,but not the move/copy constructor.Hence the example also doesn't violate the One Class-Type Conversion Rule.

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