【问题标题】:Constructors taking initializer lists构造器获取初始化列表
【发布时间】:2026-01-27 20:05:01
【问题描述】:

我明白了使用花括号进行统一初始化背后的想法。但是为什么在具有带初始化列表的构造函数的类型上使用此语法会调用该特定构造函数,即使参数仅包含在一对花括号中,即

int main(int argc, const char ** argv)
{
    vector<int> vs0{3};

    for(int v : vs0)
    {
        cout << v << ' ';
    }

    cout << '\n';

    vector<int> vs1(3);

    for(int v : vs1)
    {
        cout << v << ' ';
    }
}

/*
    Output
    3
    0 0 0
*/

为什么 vs0 是用初始化列表构造函数构造的?不应该吗

vector<int> v2{{3}};

为此?这有点令人困惑,尤其是如果您不知道一个类有一个带有初始化列表的构造函数。

【问题讨论】:

  • 是的,它令人困惑,这就是为什么它被臭名昭著地称为独角兽初始化。

标签: c++ c++11 initializer-list


【解决方案1】:

听起来您是在寻求动力,而不是标准中规定必须这样做的地方。为此,您可以查看 C++ 语言创建者 Bjarne Stroustrup 的 original proposal N1919 for intializer lists

他列出了四种初始化对象的方法:

X t1 = v; // “copy initialization” possibly copy construction
X t2(v); // direct initialization
X t3 = { v }; // initialize using initializer list
X t4 = X(v); // make an X from v and copy it to t4

请注意,他说的不是 C++11,也不是引入了初始化列表的提议版本。这又回到了 C++98 中。大括号初始化语法已经有效,但仅适用于 C 风格的结构,即没有用户定义的构造函数。这是 C 的保留,它一直允许以这种方式初始化结构(和数组),并且它总是会做同样的事情:逐个元素初始化。

提案的重点是允许以与那些 C 样式结构和数组相同的方式初始化正确的 C++ 对象,如 std::vector&lt;int&gt;:C++ 旨在允许用户定义的类看起来像内置类型(因此例如运算符重载),这是一个没有的地方。把你的问题转过来,奇怪的不是std::vector&lt;int&gt;{3} 调用了初始化列表构造函数,奇怪的是std::vector&lt;std::string&gt;{3} 调用了非初始化列表构造函数。为什么它永远调用非初始化列表构造函数?这并不是大括号初始化最初的用途。答案是允许使用手写构造函数的固定长度容器,如下所示:

class Vector3D {
public:
    Vector3D(double x, double y, double z) { /*...*/ }
    // ...
};
Vector3D v = {1, 2, 3}; // Ought to call non-initialiser list constructor

这就是为什么在使用大括号初始化时优先使用std::initializer_list 的构造函数(如果可用)。对于那些了解背景的人来说,对所有事物都使用大括号初始化器,这已经成为一种时尚,似乎真的很反常:Foo f{7} 看起来像 f 将直接包含数字 7 并且在构建完成后没有其他内容,而不是它做一些任意的事情,比如构造 7 个元素长的东西。

【讨论】:

    【解决方案2】:

    如果该类有一个采用std::initializer_list 的构造函数,那么在list initliazation 中传递一个braced-init-list 时,它将是首选。

    • 否则,T 的构造函数将分两个阶段考虑:

      • 所有将std::initializer_list 作为唯一参数的构造函数,或者如果其余参数有,则作为第一个参数 默认值,由overload resolution 检查和匹配 针对std::initializer_list

      • 类型的单个参数
      • 如果前一阶段没有产生匹配,T 的所有构造函数都参与 overload resolution 由支撑初始化列表的元素组成,具有限制 只允许非缩小转换。如果这个阶段 生成一个显式构造函数作为 a 的最佳匹配 复制列表初始化,编译失败(注意,简单 复制初始化,完全不考虑显式构造函数)。

    {3} 是一个花括号初始化列表,那么 vector 将被初始化为包含 1 个值为 3 的元素。该行为与传递{1, 2}{1, 2, 3}等一致。

    【讨论】:

      最近更新 更多