c++ - 为什么initializer_list没有违反 "One Class-Type Conversion"的规则

标签 c++ c++11

"One Class-Type Conversion"Rule 引用自 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

上面的代码因为违反了“一类类型转换”的规则而导致错误; 这是Implicit conversions的介绍
基于上面的规则,我在学习c++的过程中遇到了2道题,都是copy initialization,也就是说copy constructor会被调用。

string 的示例

考虑代码片段 string a="hello"; ,如果我们不考虑编译器经常会做一些优化,比如copy elision,那么代码会被解释成:

  1. 转换"hello"的类型来自 char[6]char* ; (转换 1)
  2. 调用string(char*)的构造函数并生成临时 string对象 temp (转换 2)
  3. 调用string(string&&)的拷贝构造函数, 最后得到 a

问题:好的,实际上发生了2次转换,为什么不违反“一类类型转换”规则?是不是因为从char[6]转换过来的至 char*不是类类型转换,因为char*不是类类型而是内置类型

initializer_list 的示例

一旦我们使用 vector 的列表初始化喜欢

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

然后代码被解释为:

  1. {1,2,3,4,5}没有像“hello”这样的类型,所以没有像 char[6] 这样的转换至 char*
  2. 转换{1,2,3,4,5}initializer_list<int> temp 的临时对象(转换 1)
  3. 转换 initializer_list<int> tempvector<int> temp1通过使用:(转换 2)

    vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
    
  4. 调用vector( vector&& other );的拷贝构造函数,最后我们得到 vector<int> example ;

问题:糟糕!还有 2 次转化 发生了!而且都是class-type的转换!发生了什么事?

最佳答案

好的,因为一个小时过去了,这里没有人回答这个问题,我自己找到了答案。

字符串示例

原因是char[5]char*不是class-type 转换,因此它不违反One Class-Type Conversion 规则。

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.

initializer_list 示例

initializer_list是一个特例,实际上是copy-list-initialization而不是copy-initialization,所以这里没有2类型转换。参见 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

所以当我们使用一个 initializer_list 时,没有从 other 到 T 的转换,这是 copy-constructor 的要求。参见 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)

所以参数为initializer_list<int>的构造函数将被直接调用,而不是 move/copy构造函数。因此该示例也不违反一个类类型转换规则。

关于c++ - 为什么initializer_list没有违反 "One Class-Type Conversion"的规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49205529/

相关文章:

c++ - 使用 libharu 将 PNG 图像从文件加载到内存缓冲区

C++ 为几乎相同的代码提供不同的输出

c++ - 'function1' 和 'function2' 之间的歧义 (C++)

c++ - 具有相同名称/命名空间但成员不同的结构

c++ - FizzBu​​zz.cpp 与 lambdas?

c++ - 原子线程计数器

c++ - 打印以在c++中在线的特定位置输出

c++ - 关于cerr cout和clog的问题

c++ - 为什么右值引用参数更喜欢 const 左值引用而不是右值引用参数?

c++ - 定义函数时,lambda 函数/表达式是什么类型?