为什么initializer_list不违反“一类转换”规则

时间:2018-03-10 05:04:38

标签: c++ c++11

“One Class-Type Conversion”规则引自c ++ Primer5th§7.5.4

  

仅允许一种类型转换
  在第4.11.2节(第162页)中,我们注意到编译器将自动仅应用一个类类型转换。例如,以下代码出错,因为它隐式使用了两次转换:

     //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 ++时遇到了两个问题,它们都是复制初始化,这意味着将调用复制构造函数

string

的示例

考虑代码片段string a="hello";,如果我们不考虑编译器经常进行某些优化的事实,例如 copy elision ,那么代码将被解释为:

  1. "hello"的类型从char[6]转换为char*; (转换1)
  2. 调用string(char*)的构造函数并生成临时string对象temp (转换为2)
  3. 调用string(string&&)的复制构造函数,最后得到a
  4. 问题好的,实际上 2转换会发生,为什么这不会违反“One Class-Type Conversion”规则?是因为从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> temp转换为vector<int> temp1 :( 转换2

      vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
      
    4. 调用vector( vector&& other );的复制构造函数,最后得到vector<int> example;
    5. 问题:哎呀!还有 2次转化!并且有 class-type 的所有转换!发生了什么事?

1 个答案:

答案 0 :(得分:2)

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

字符串

的示例

共鸣是char[5]char*不是类型转换,所以它不违反 One Class-Type Conversion 规则。

  

转化顺序
  隐式转换序列按以下顺序组成:
  1)零个或一个标准转换序列;
  2)零或一个用户定义的转换;
  3)零个或一个标准转换序列   在考虑构造函数或用户定义的转换函数的参数时,只允许一个标准转换序列(否则可以有效地链接用户定义的转换)。从一种内置类型转换为另一种内置类型时,只允许一个标准转换序列。

initializer_list

的示例

initializer_list是一个特殊情况,实际上是 copy-list-initializaition 但不是 copy-initialization ,因此没有 2类型转换。见list initialization

  
      
  • 否则,T的构造函数分两个阶段考虑:
      检查将std :: initializer_list作为唯一参数的所有构造函数,或者作为第一个参数(如果其余参数具有默认值),并针对std ::类型的单个参数进行重载匹配。 initializer_list
  •   

因此,当我们使用initializer_list时,没有从其他转换为T ,这是 copy-constructor 的要求。见Copy initialization

  

如果T是类类型,并且其他类型的cv-nonqualified版本不是T或从T派生,或者如果T是非类型类型,但是其他类型是类类型,则用户 - 可以检查定义的转换序列,它们可以从类型的其他转换为T (或者从T派生的类型,如果T是类类型并且转换函数可用),并且通过以下方式选择最佳的转换序列超载分辨率。 转换的结果,这是一个prvalue临时(直到C ++ 17)prvalue表达式(自C ++ 17),如果使用转换构造函数,则用于直接初始化对象。最后一步通常是优化的,转换的结果直接在为目标对象分配的内存中构造,但是相应的构造函数(移动或复制)需要可访问,即使它不是用过的。 (直到C ++ 17)

所以带参数initializer_list<int>的构造函数将被直接调用,但不会被move/copy构造函数调用。虽然示例也没有违反 One Class-Type Conversion 规则。