【发布时间】:2017-10-04 15:46:29
【问题描述】:
我知道这是一个已在 stackoverflow 上广泛讨论的主题,但我很难找到彻底的答案来消除我对 C++ 中的列表初始化和 initializer_lists 的所有困惑,所以我将尝试一下并提出我自己的问题。
请考虑以下 sn-p 代码:
class C
{
public :
C(int a, int b, int c) : _a (a), _b(b), _c(c) {}; //initialization_list with ()
//C(int a, int b, int c) : _a{ a }, _b{ b }, _c{ c } {}; //initialization list with {}
private :
int _a, _b, _c;
};
int main()
{
C a(5.3,3.3,4.3); // no list
C b{5.3,3.3,4.3}; // list {}
C c({5.3,3.3,4.3}); // list {}
}
我不明白为什么这两个初始化列表的行为相似?我期待,当尝试使用_a{a}, _b{b}, _c{c} 类型的初始化列表创建 C 类型的对象时,会出现关于缩小的编译器错误。但是,不会产生错误,_a, _b and _c 只存储整数值。
只有在使用列表“{}”创建对象 b 或 c 时,编译器才会生成缩小错误消息。这是为什么?使用我不知道的 {} 或 () 编写初始化列表之间是否有任何区别,或者行为是否相同?
来我的下一个问题:
class C
{
public :
//private :
int _a, _b, _c;
};
int main()
{
C a(5,3,4); //obviously doesn't work as no such constructor
C b{5,3,4}; //work only if _a, _b and _c are not private nor protected!
}
为什么第二个语句(带大括号)只有在变量是公共的情况下才有效?所涉及的机制是什么?
所以我想更好地理解,除了通过创建具有列表 {} 的对象所提供的“缩小安全性”之外,此列表还有哪些其他“功能”机制提供?因为在第二次调用中,它仍然是被调用的默认构造函数(因此,不是以 initializer_list 作为参数的默认构造函数),对吧?
最后,想象一下在我的class C 中,我有另一个构造函数将初始化列表作为参数。
class C
{
public :
C() = default; //default constructor
C(int a, int b, int c) : _a (a), _b(b), _c(c) {};
C(std::initializer_list<int> a) { //do some stuffs with the list};
private :
int _a, _b, _c;
};
很明显,如果尝试创建一个接受除 3(或实际上是 0)以外的任何整数的对象,将调用采用 initializer_list 的构造函数。但是,如果创建这样的对象:
C c();
或
C c{};
将调用默认构造函数。但是,如果创建一个恰好具有 3 个整数的对象:
C c(5,2,3);
或
C c{5,2,3};
initializer_list 构造函数将被调用。规则是这样的:
- 如果可以调用默认构造函数或初始化列表构造函数,则首选默认构造函数
- 如果可以调用初始化列表构造函数和“普通构造函数”,则首选初始化列表构造函数
因此(如果我错了,请纠正我),如果我这样创建我的对象:
C c{5,3,4};
初始化器列表构造函数将被调用。但是,如果我这样创建对象:
C c(5,3,4);
将调用第二个构造函数(以 3 个整数作为参数)。我的问题是:如果我还想提供缩小安全性,如何使用第二个构造函数而不是初始化器列表创建一个对象? (因为如果我像这个问题的第一个例子那样做,初始化列表构造函数将被调用!)。
不要犹豫,举例说明你的回复,并讨论我在这个问题中没有谈到的与列表相关的概念。我想很好地掌握这些。谢谢。
【问题讨论】:
-
您似乎将constructor member initializer lists、
std::initializer_list和list initialization 混合在一起。这是三种不同的东西。