我知道这是一个在stackoverflow上已经讨论得非常广泛的主题,但是我很难找到彻底的解答来消除我在C ++中列表初始化和initializer_lists的所有困惑,所以我只想试一试并问我自己的问题。
请考虑以下代码段:
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时,我期待得到有关缩小的编译器错误。但是,不会生成错误,_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};
将调用iniatializer-list构造函数。但是,如果我像这样创建我的对象:
C c(5,3,4);
将调用第二个构造函数(以3个int作为参数)。我的问题是:如果我想提供缩小的安全性,如何使用第二个构造函数而不是iniatializer-list创建一个对象? (因为如果我在这个问题的第一个例子中那样做,那么将调用初始化列表构造函数!)。
请毫不犹豫地举例说明你的回复,并讨论我在这个问题上没有谈到的与列表相关的概念。我想很好地掌握这些。感谢。
答案 0 :(得分:1)
因此,无论何时使用花括号,您都在使用aggregate initialization,一种初始化结构或类的方法,按顺序初始化,或通过指示符。例如,
#include <iostream>
struct Foo
{
int a;
char b;
};
class Doo
{
public:
double h;
char ch;
};
int main() {
Foo first = {3, 't'};
std::cout << first.b << "\n";
//t
Doo second = {'3', 50};
std::cout << second.ch << "\n";
//2 interpreted as char
}
这里,当我们使用{}
初始化一个类或结构时,它们总是被解释为按照类中列出的顺序。这就是打印'2'的原因,因为ASCII中的50对应于字符'2'。
构造函数初始化
因此,您也可以使用与构造函数初始化列表相同的逻辑,
#include <iostream>
struct Pair
{
int first;
long second;
};
class Couple
{
public:
Pair p;
int g;
public:
Couple(): p{3, 700}, g{3}
{}
};
int main() {
Couple test;
std::cout << test.p.first << "\n";
//3
}
此处,{3, 700}
旁边的p
与代码中使用的Pair p = {3, 700};
相同。你基本上使用了一个聚合初始化的命令。现在,如果我们将Pair字段的花括号更改为括号会发生什么?
我们收到此错误
main.cpp: In constructor 'Couple::Couple()':
main.cpp:15:26: error: no matching function for call to 'Pair::Pair(int, int)'
Couple(): p(3, 700), g{3}
那是因为我们没有Pair的构造函数接受两个数字。因此,聚合初始化和括号之间的关键区别是,您需要为使用括号创建的任何特定参数集实现构造函数,但使用花括号,您可以使用编译器提供的默认参数。
std :: initializer_list是一个不常用的容器形式,用于{}
初始化列表中的多个参数。