我试图了解C++ STL容器的初始化。这是我的噩梦:
vector<int> V0 ({ 10, 20 }); // ok - initialized with 2 elements
vector<int> V1 = { 10, 20 }; // ok - initialized with 2 elements
vector<int> V2 = {{ 10, 20 }}; // ok - initialized with 2 elements
vector<int> V3 = {{ 10 }, 20 }; // ok - initialized with 2 elements
vector<int> V4 { 10, 20 }; // ok - initialized with 2 elements
vector<int> V5 {{ 10, 20 }}; // ok - initialized with 2 elements
vector<int> V6 {{ 10 }, 20 }; // ok - initialized with 2 elements
queue<int> Q0 ({ 10, 20 }); // ok - initialized with 2 elements
// queue<int> Q1 = { 10, 20 }; // compile error
// queue<int> Q2 = {{ 10, 20 }}; // compile error
// queue<int> Q3 = {{ 10 }, 20 }; // compile error
// queue<int> Q4 { 10, 20 }; // compile error
queue<int> Q5 {{ 10, 20 }}; // ok - initialized with 2 elements
// queue<int> Q6 {{ 10 }, 20 }; // compile error
我们正在谈论C++ 11。
我做了一些研究,这是我的问题:
queue<T>
的initializer_list
构造函数而导致的。参见vector<T>
:http://www.cplusplus.com/reference/vector/vector/vector/和queue<T>
:http://www.cplusplus.com/reference/queue/queue/queue/对吗?
V0
到V6
的所有 vector ,我都了解V0
,V1
和V4
。有人可以帮助我理解V2
,V3
,V5
和V6
吗? Q0
或Q5
。有人能帮我吗? 我还在阅读Mike Lui的文章:Initialization in C++ is Seriously Bonkers。我想与大家分享一下,但是有没有一种快速的方法可以帮助我了解这场噩梦? :-)
最佳答案
没有任何“噩梦”。您只需要阅读您所写的内容。更具体地说,您必须系统地从外部处理规则。
vector<int> V0 ({ 10, 20 });
调用
vector
构造函数(这就是()
的意思),并向其传递单个braced-init-list。因此,它将选择一个具有一个值的构造函数,但仅选择其第一个参数可以由包含整数的2元素braced-init-list初始化的构造函数。例如initializer_list<int>
包含的vector<int>
构造函数。vector<int> V1 = { 10, 20 };
列表初始化(当您直接使用braced-init-list初始化事物时会发生这种情况)。在列表初始化规则下,首先考虑带有单个
initializer_list
参数的所有类型的构造函数。系统尝试直接使用braced-init-list初始化这些构造函数。如果可以使用候选构造函数之一成功,则调用该构造函数。显然,您可以从2元素的整数括号初始列表中初始化
initializer_list<int>
。这是initializer_list
中唯一的vector
构造函数,因此被调用。vector<int> V2 = {{ 10, 20 }};
仍然进行列表初始化。同样,将考虑与braced-init-list中的值匹配的
initializer_list
构造函数。但是,括号初始列表中的“值”本身就是另一个括号初始列表。不能从2元素的braced-init-list初始化int
,因此initializer_list<int>
不能初始化{{10, 20}}
。由于无法使用
initializer_list
构造函数,因此在正常函数重载解析规则下考虑所有构造函数。在这种情况下,(外部)braced-init-list的成员被视为该类型的构造函数的参数。在外部的braced-init-list中只有一个值,因此仅考虑可以使用一个参数调用的构造函数。系统将尝试使用内部的braced-init-list初始化所有此类构造函数的第一个参数。还有一个构造函数,其参数可以由2元素的整数支撑初始列表初始化。即,
initializer_list<int>
构造函数。也就是说,虽然initializer_list<int>
不能使用{{10, 20}}
进行初始化,但可以仅使用{10, 20}
进行初始化。vector<int> V3 = {{ 10 }, 20 };
同样,仍然列出初始化。再一次,我们首先尝试将完整的braced-init-list应用到该类型的任何
initializer_list
构造函数。可以从initializer_list<int>
的大括号初始化列表中初始化{{10}, 20}
吗?是的。这就是发生的情况。为什么这样做?因为任何可复制/移动的
T
类型始终可以从包含该类型值的大括号初始列表中初始化。也就是说,如果T t = some_val;
起作用,那么T t = {some_val};
也会起作用(除非T
具有使用initializer_list
的T
构造函数,这肯定是很奇怪的)。如果T t = {some_val};
起作用,那么initializer_list<T> il = {{some_val}};
也会起作用。vector<int> V4 { 10, 20 }; // ok - initialized with 2 elements
vector<int> V5 {{ 10, 20 }}; // ok - initialized with 2 elements
vector<int> V6 {{ 10 }, 20 }; // ok - initialized with 2 elements
它们与1、2和3相同。列表初始化通常称为“统一初始化”,因为直接使用braced-init-list和
= braced-init-list
之间几乎没有区别。唯一的区别是,是否选择了显式构造函数,或者您是否正在使用在braced-init-list中具有单个值的auto
。queue
的initializer_list
构造函数未“丢失”。它不是故意存在的,因为queue
不是容器。它是容器适配器类型。它存储一个容器,并使该容器的接口(interface)适应于仅限队列操作:插入,弹出和查看。因此,这些都不应该起作用。
queue<int> Q0 ({ 10, 20 });
就像
queue<int>
一样,它使用常规的旧的重载分辨率调用V0
的构造函数。唯一的区别是,它选择的构造函数是采用队列的容器类型的构造函数。由于未指定容器,因此它使用默认值:std::deque<int>
,可以从两个整数的braced-init-list构造它。queue<int> Q5 {{ 10, 20 }};
与
V2
相同的情况。 queue
上没有initializer_list构造函数,因此它的行为与Q0
完全相同:使用重载分辨率来选择一个构造函数,该构造函数的参数可以采用2个整数的大括号初始列表。
关于c++ - 试图了解C++ STL容器的初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59559162/