【问题标题】:What language rules permit C++11 to deduce that this is an initializer_list of pairs?哪些语言规则允许 C++11 推断这是一个 initializer_list 对?
【发布时间】:2014-07-13 00:41:31
【问题描述】:

在 C++11 中,初始化 std::map<std::string, int> 似乎是合法的,如下所示:

std::map<std::string, int> myMap = {
     { "One",   1 },
     { "Two",   2 },
     { "Three", 3 }
};

直观地说,这是有道理的 - 大括号括起来的初始值设定项是字符串对的列表,std::map&lt;std::string, int&gt;::value_typestd::pair&lt;std::string, int&gt;(可能带有一些 const 资格。

但是,我不确定我是否理解此处的打字工作原理。如果我们消除这里的变量声明并且只使用大括号括起来的初始化程序,编译器将不会知道它正在查看std::initializer_list&lt;std::pair&lt;std::string, int&gt;&gt;,因为它不会知道大括号对代表std::pairs。因此,似乎编译器以某种方式推迟了将类型分配给大括号封闭的初始化程序的行为,直到它从 std::map 构造函数中获得足够的类型信息以意识到嵌套的大括号是用于对的。我不记得在 C++03 中发生过这样的事情;据我所知,表达式的类型从不依赖于它的上下文。

哪些语言规则允许此代码正确编译并允许编译器确定初始化列表使用什么类型?我希望得到具体参考 C++11 规范的答案,因为这真的很有趣!

谢谢!

【问题讨论】:

  • C++03 中的大括号括起来的列表已经非常特定于初始化程序上下文。这里与使用大括号初始化的 C++03 嵌套聚合(可能是 std::pair&lt;std::string, int&gt; myArray[])没有显着区别,只是类型不是直接命名的,而是从可用的构造函数中推断出来的。
  • 顺便说一句,隐式转换运算符(成员operator T())是上下文确定表达式类型(并用于重载解析)的另一个地方。
  • braced-init-list 不是表达式,也没有类型。没有推论,只有初始化列表构造函数(其中只有一个)之间的重载决议

标签: c++ c++11 language-lawyer initializer-list list-initialization


【解决方案1】:

在表达式中

std::map<std::string, int> myMap = {
     { "One",   1 },
     { "Two",   2 },
     { "Three", 3 }
};

在右侧有一个 braced-init-list,其中每个元素也是一个 braced-init-list。发生的第一件事是考虑std::map 的初始化列表构造函数。

map(initializer_list<value_type>,
    const Compare& = Compare(),
    const Allocator& = Allocator());

map&lt;K, V&gt;::value_typepair&lt;const K, V&gt; 的类型定义,在本例中为pair&lt;const string, int&gt;。内部花括号初始化列表可以成功转换为map::value_type,因为std::pair 有一个构造函数,该构造函数引用其两个组成类型,而std::string 有一个隐式转换构造函数,它接受char const *

因此std::map 的初始化列表构造函数是可行的,并且可以从嵌套的braced-init-lists 中进行构造。

§13.3.1.7/1 [over.match.list]

中有相关的标准语

当非聚合类类型T 的对象被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:
— 最初,候选函数是类 T 的初始化列表构造函数 (8.5.4),参数列表由初始化列表作为单个参数组成。
— 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T 的所有构造函数,参数列表由初始化列表的元素组成。

第一个项目符号导致mapinitializer_list 构造函数被选择用于外部支撑初始化列表,而第二个项目符号导致选择正确的pair 构造函数用于内部支撑-初始化列表。

【讨论】:

    【解决方案2】:

    这是列表初始化。规则见标准 §8.5.4[dcl.init.list]/p3:

    类型 T 的对象或引用的列表初始化定义为 如下:

    • 如果初始化列表没有元素,并且 T 是具有默认构造函数的类类型,则对象是值初始化的。
    • 否则,如果 T 是聚合,则执行聚合初始化 (8.5.1)。 [省略示例]
    • 否则,如果 T 是 std::initializer_list&lt;E&gt; 的特化,则按如下所述构造 initializer_list 对象,并且 用于根据规则初始化对象 从相同类型的类中初始化对象 (8.5)。
    • 否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数并选择最佳的构造函数 通过重载决议(13.3、13.3.1.7)。如果收窄 转换(见下文)需要转换任何参数, 程序格式不正确。
    • [省略规则的示例和其余部分]

    请注意,在这些情况下,重载决议将优先使用 std::initializer_list 构造函数(§13.3.1.7 [over.match.list])。

    因此,当编译器看到一个用于初始化非聚合、非std::initializer_list 类类型的对象的花括号列表时,它将执行重载决策以选择适当的构造函数,如果可行,则首选initializer_list 构造函数存在一个(就像 std::map 一样)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 2023-04-10
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 2012-11-29
      • 2016-07-11
      相关资源
      最近更新 更多