[英]Trying to Understand the Initialization of C++ STL Containers
I am trying to understand the initialization of C++ STL containers.我想了解 C++ STL 容器的初始化。 Here is the nightmare I got:
这是我的噩梦:
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
We are talking about C++11.我们正在谈论 C++11。
I did some research, and here are my questions:我做了一些研究,这里是我的问题:
queue<T>
's initializer_list
constructor is missing.queue<T>
的initializer_list
构造函数丢失造成的。 See vector<T>
: http://www.cplusplus.com/reference/vector/vector/vector/ and queue<T>
: http://www.cplusplus.com/reference/queue/queue/queue/ am I right?vector<T>
: http : //www.cplusplus.com/reference/vector/vector/vector/和queue<T>
: http: //www.cplusplus.com/reference/queue/queue/queue/ 我是对?V0
to V6
, I understand V0
, V1
, V4
.V0
到V6
所有向量,我理解V0
、 V1
、 V4
。 Can someone help me understand V2
, V3
, V5
and V6
?V2
、 V3
、 V5
和V6
吗?Q0
or Q5
.Q0
或Q5
。 Can someone help me? I am also reading Mike Lui's article: Initialization in C++ is Seriously Bonkers .我也在阅读 Mike Lui 的文章: Initialization in C++ is Seriously Bonkers 。 I'd like to share it with you guys, but is there a quick way to help me understand this nightmare?
我想和你们分享一下,但是有没有一种快速的方法可以帮助我理解这个噩梦? :-)
:-)
There is nothing "nightmarish" about any of that.没有任何“噩梦”。 You simply need to read what you've written.
你只需要阅读你写的东西。 More specifically, you have to work through the rules from the outside in, systematically.
更具体地说,您必须从外向内系统地处理规则。
vector<int> V0 ({ 10, 20 });
Calls a vector
constructor (that's what the ()
mean), passing it a single braced-init-list.调用一个
vector
构造函数(这就是()
意思),传递给它一个花括号初始化列表。 Therefore, it will pick a constructor that takes one value, but only the constructor whose first parameter can be initialized by a 2-element braced-init-list containing integers.因此,它将选择一个接受一个值的构造函数,但只有第一个参数可以由包含整数的 2 元素花括号初始化列表初始化的构造函数。 Such as the
initializer_list<int>
constructor that vector<int>
contains.例如
vector<int>
包含的initializer_list<int>
构造函数。
vector<int> V1 = { 10, 20 };
List initialization (that's what happens when you initialize a thing with a braced-init-list directly).列表初始化(这就是直接使用花括号初始化列表初始化事物时发生的情况)。 Under list-initialization rules, all constructors of the type which take a single
initializer_list
parameter are considered first.在列表初始化规则下,首先考虑采用单个
initializer_list
参数类型的所有构造函数。 The system attempts to initializer these constructors with the braced-init-list directly;系统尝试直接使用braced-init-list 初始化这些构造函数; if it can succeed with one of the candidate constructors, then that constructor is called.
如果它可以使用候选构造函数之一成功,则调用该构造函数。
Obviously, you can initialize an initializer_list<int>
from a 2-element braced-init-list of integers.显然,您可以从整数的 2 元素花括号初始化器列表初始化一个
initializer_list<int>
。 And this is the only initializer_list
constructor in vector
, so it gets called.这是
vector
唯一的initializer_list
构造函数,因此它被调用。
vector<int> V2 = {{ 10, 20 }};
Still list initialization.仍然是列表初始化。 Again,
initializer_list
constructors which match the values in the braced-init-list are considered.再次考虑与括号初始化列表中的值匹配的
initializer_list
构造函数。 However, the "value" in the braced-init-list is itself another braced-init-list.但是,花括号初始化器列表中的“值”本身就是另一个花括号初始化器列表。
int
cannot be initialized from a 2-element braced-init-list, so the initializer_list<int>
cannot be initialized by {{10, 20}}
. int
不能从 2 元素的花括号初始化器列表initializer_list<int>
,因此initializer_list<int>
不能被{{10, 20}}
初始化。
Since no initializer_list
constructor can be used, all constructors are considered under normal function overload resolution rules.由于不能使用
initializer_list
构造函数,因此所有构造函数都在正常的函数重载解析规则下考虑。 In this case, the members of the (outer) braced-init-list are considered arguments to the constructors of the type.在这种情况下,(外部)花括号初始化列表的成员被视为该类型构造函数的参数。 There is only one value in the outer braced-init-list, so only constructors which can be called with one argument are considered.
外层括号初始化列表中只有一个值,因此只考虑可以使用一个参数调用的构造函数。
The system will attempt to initialize the first parameter of all such constructors with the inner braced-init-list.系统将尝试使用内部花括号初始化列表初始化所有此类构造函数的第一个参数。 And there is a constructor whose parameter can be initialized by a 2-element braced-init-list of integers.
并且有一个构造函数,其参数可以由整数的 2 元素花括号初始化列表初始化。 Namely, the
initializer_list<int>
constructor.即,
initializer_list<int>
构造函数。 That is, while initializer_list<int>
can't be initialized by {{10, 20}}
, it can be initialized by just {10, 20}
.也就是说,虽然
initializer_list<int>
不能被{{10, 20}}
初始化,但它只能被{10, 20}
初始化。
vector<int> V3 = {{ 10 }, 20 };
Again, still list initialization.同样,仍然列出初始化。 So again, we first attempt to apply the full braced-init-list to any
initializer_list
constructors of the type.因此,我们再次尝试将完整的花括号初始化列表应用于该类型的任何
initializer_list
列表构造函数。 Can initializer_list<int>
be initialized from a braced-init-list of {{10}, 20}
?可以从
{{10}, 20}
的花括号初始化器列表initializer_list<int>
吗? Yes.是的。 So that's what happens.
所以这就是发生的事情。
Why does this work?为什么这样做? Because any type
T
which is copyable/moveable can always be initialized from a braced-init-list containing some value of that type.因为任何可复制/可移动的类型
T
始终可以从包含该类型的某些值的花括号初始化列表进行初始化。 That is, if T t = some_val;
也就是说,如果
T t = some_val;
works, then so too does T t = {some_val};
有效,那么
T t = {some_val};
也是如此T t = {some_val};
(unless T
has an initializer_list
constructor that takes T
, which would be decidedly weird). (除非
T
有一个带T
的initializer_list
构造函数,这肯定很奇怪)。 And if T t = {some_val};
如果
T t = {some_val};
works, then so too does initializer_list<T> il = {{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
These are identical to 1, 2, and 3. List initialization is often called "uniform initialization" because there is (almost) no difference between using the braced-init-list directly and using = braced-init-list
.它们与 1、2 和 3 相同。列表初始化通常称为“统一初始化”,因为直接使用
= braced-init-list
和使用= braced-init-list
之间(几乎)没有区别。 The only times there is a difference is if an explicit constructor is selected or if you're using auto
with a single value in the braced-init-list.唯一不同的是,是否选择了显式构造函数,或者您是否使用带有括号初始化列表中的单个值的
auto
。
queue
s initializer_list
constructor is not "missing". queue
的initializer_list
构造函数没有“丢失”。 It doesn't exist on purpose because queue
is not a container .它不是故意存在的,因为
queue
不是container 。 It is a container adapter type.它是一种容器适配器类型。 It stores a container and adapts the container's interface to be restricted to queue operations: push, pop, and peek.
它存储一个容器并调整容器的接口以限制队列操作:push、pop 和 peek。
So none of those are supposed to work.所以这些都不应该起作用。
queue<int> Q0 ({ 10, 20 });
This calls a constructor of queue<int>
using regular old overload resolution, just like V0
.这使用常规的旧重载解析调用
queue<int>
的构造函数,就像V0
一样。 The only difference is that the constructor it selects is the one that takes the container type for the queue.唯一的区别是它选择的构造函数是采用队列容器类型的构造函数。 Since you didn't specify a container, it uses the default:
std::deque<int>
, which can be constructed from a braced-init-list of two integers.由于您没有指定容器,它使用默认值:
std::deque<int>
,它可以从两个整数的花括号初始化列表构造。
queue<int> Q5 {{ 10, 20 }};
Same situation as V2
.与
V2
情况相同。 There are no initializer_list constructors on queue
, so it acts exactly like Q0
: using overload resolution to select a constructor whose argument can take a braced-init-list of 2 integers. queue
上没有 initializer_list 构造函数,所以它的作用与Q0
完全一样:使用重载决议来选择一个构造函数,其参数可以采用 2 个整数的花括号初始化列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.