【问题标题】:C++11 uniform initialization: ambiguity between initializer list and multiple-parameter constructors?C++11 统一初始化:初始化列表和多参数构造函数之间的歧义?
【发布时间】:2020-10-01 00:16:53
【问题描述】:

目前我正试图围绕 C++11 的统一初始化。我遇到了这种模棱两可的情况:考虑一个可以从两个参数的构造函数或任意长度的初始化列表构造的类:

class Foo {
  public:
    Foo(int a, int b) {
      std::cout << "constructor 1" << std::endl;
    }
    Foo(std::initializer_list<int>) {
      std::cout << "constructor 2" << std::endl;
    }
};

遵循统一的初始化约定,我希望以下工作:

Foo a (1, 2) 打印 constructor 1 (duh)

Foo b {1, 2} 打印 constructor 1

Foo c = {1, 2} 打印 constructor 2

但是,编译器似乎将 Foo b {1, 2} 解释为列表初始化,并调用构造函数 2。() 语法是强制编译器在初始化列表构造函数时考虑其他类型的构造函数的唯一方法吗?在场吗?

【问题讨论】:

标签: c++ c++11


【解决方案1】:

编译器似乎将 Foo b {1, 2} 解释为列表 初始化,并调用构造函数 2. () 语法是唯一的方法吗 强制编译器考虑其他类型的构造函数 初始化列表构造函数是否存在?

标准草案的引文很好地解释了这一点:

9.4.5.2 [dcl.init.list](强调我的):

一个构造器是一个初始化列表构造器,如果它的第一个 参数类型为 std​::​initializer_list 或对 cv 的引用 某些类型 E 的 std​::​initializer_list,或者没有 其他参数,否则所有其他参数都有默认参数 ([dcl.fct.default])。

[注 2:Initializer-list 构造函数是 在列表初始化中优于其他构造函数 ([over.match.list])。将初始化列表作为参数传递给 类 C 的构造函数模板模板 C(T) 不 创建一个初始化列表构造函数,因为初始化列表 参数导致相应的参数是非推导的 上下文([temp.deduct.call])。 ——尾注]

12.4.2.8 [over.match.list]:

当非聚合类类型 T 的对象被列表初始化时 [dcl.init.list] 指定执行重载决议 根据本条中的规则或在形成 根据 [over.ics.list] 的列表初始化序列,重载 resolution 分两个阶段选择构造函数:

  • 如果初始化列表不为空或 T 没有默认构造函数, 重载解析首先在候选函数的地方执行 是类 T 的初始化列表构造函数 ([dcl.init.list]) 并且参数列表由初始化列表组成 论据。

  • 否则,或如果找不到可行的初始化列表构造函数再次执行重载决议,其中 候选函数 是类 T 的所有构造函数,参数列表包括 初始化列表的元素。

【讨论】:

    【解决方案2】:

    您可以在构造函数中添加一个额外的忽略参数,以在调用点指定特定的重载,就像在 STL 中所做的那样:

    #include <iostream>
    
    struct non_init_list_t {};
    inline constexpr non_init_list_t non_init_list;
    
    struct Class {
        Class(int a, int b, non_init_list_t = non_init_list) { std::clog << "()\n"; }
        Class(std::initializer_list<int> list) { std::clog << "{}\n"; }
    };
    
    Class a{12, 42, non_init_list};  // ()
    Class b{12, 42};                 // {}
    Class c(12, 42);                 // ()
    

    【讨论】:

      【解决方案3】:

      如果构造函数有initializer_list版本,编译器会首先将其解释为initializer_list,如果没有initializer_list版本,编译器会将其解释为另一个重载版本。 如果编译器将其解释为另一个版本,并且您想调用使用 initializer_list 版本的构造函数,则恰好参数的数量和类型与其他 ctor 相同,那么您将导致错误。那么编译器选择initializer_list版本还是其他版本呢?所以使用括号表示法绝对不是initializer_list 版本。如果您的构造函数中没有initializer_list 版本,请不要担心这个问题。

      顺便说一句,如果您使用auto 自动推断类型,请不要使用统一初始化。它必须将类型解释为initializer_list

      【讨论】:

        猜你喜欢
        • 2016-07-19
        • 1970-01-01
        • 1970-01-01
        • 2012-03-28
        • 2021-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多