简体   繁体   English

尝试理解 C++ STL 容器的初始化

[英]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:我做了一些研究,这里是我的问题:

  1. I believe the compile error is caused by the fact that 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/ 我是对?
  2. Now for all the vectors from V0 to V6 , I understand V0 , V1 , V4 .现在对于从V0V6所有向量,我理解V0V1V4 Can someone help me understand V2 , V3 , V5 and V6 ?有人能帮我理解V2V3V5V6吗?
  3. I don't quite understand Q0 or Q5 .我不太明白Q0Q5 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有一个带Tinitializer_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". queueinitializer_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM