构造函数采用初始化列表
Constructors taking initializer lists
我明白了用花括号统一初始化背后的想法。但是,为什么在具有采用初始化列表的构造函数的类型上使用此语法,调用该特定构造函数,即使参数仅包含在一对大括号中,即
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}};
为此?这有点令人困惑,特别是如果您不知道 class 有一个采用初始化列表的构造函数。
如果 class 有一个采用 std::initializer_list
的构造函数,那么在 braced-init-list 中传递它时将是首选21=].
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
If the previous stage does not produce a match, all constructors of T
participate in overload resolution against the set of arguments that
consists of the elements of the braced-init-list, with the restriction
that only non-narrowing conversions are allowed. If this stage
produces an explicit constructor as the best match for a
copy-list-initialization, compilation fails (note, in simple
copy-initialization, explicit constructors are not considered at all).
{3}
是花括号初始化列表,那么 vector
将被初始化为包含 1 个值为 3
的元素。该行为与传递 {1, 2}
、{1, 2, 3}
等一致。
这听起来像是在寻求动力,而不是标准中规定必须这样做的地方。为此,您可以查看 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<int>
:C++ 旨在允许用户定义 类 看起来像内置类型(因此例如运算符重载),这是它没有的地方。为了解决您的问题,奇怪的不是 std::vector<int>{3}
调用初始化列表构造函数,奇怪的是 std::vector<std::string>{3}
调用非初始化列表构造函数。为什么它 ever 调用非初始化列表构造函数?这并不是大括号初始化最初的目的。答案是允许带有手写构造函数的定长容器,像这样:
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 个元素长的东西。
我明白了用花括号统一初始化背后的想法。但是,为什么在具有采用初始化列表的构造函数的类型上使用此语法,调用该特定构造函数,即使参数仅包含在一对大括号中,即
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}};
为此?这有点令人困惑,特别是如果您不知道 class 有一个采用初始化列表的构造函数。
如果 class 有一个采用 std::initializer_list
的构造函数,那么在 braced-init-list 中传递它时将是首选21=].
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 typestd::initializer_list
If the previous stage does not produce a match, all constructors of
T
participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization, compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all).
{3}
是花括号初始化列表,那么 vector
将被初始化为包含 1 个值为 3
的元素。该行为与传递 {1, 2}
、{1, 2, 3}
等一致。
这听起来像是在寻求动力,而不是标准中规定必须这样做的地方。为此,您可以查看 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<int>
:C++ 旨在允许用户定义 类 看起来像内置类型(因此例如运算符重载),这是它没有的地方。为了解决您的问题,奇怪的不是 std::vector<int>{3}
调用初始化列表构造函数,奇怪的是 std::vector<std::string>{3}
调用非初始化列表构造函数。为什么它 ever 调用非初始化列表构造函数?这并不是大括号初始化最初的目的。答案是允许带有手写构造函数的定长容器,像这样:
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 个元素长的东西。