Why does &#39;std::vector<int> b{2};&#39; create a 1-element vector, and not a 2-element one?

泪湿孤枕 提交于 2019-11-26 14:12:42

问题


I've been playing around with C++11 for the past few days, and I came up with something strange.

If I want to uniformly initialize an int:

int a{5};

But if I do the same thing to a std::vector:

std::vector<int> b{2};

Does not construct a two element array, but rather an array with one element of value two. It seems like to get that effect one would need to be more explicit about it:

std::vector<int> c{{2}};
std::vector<int> d = {2};

But not like the declaration of b - this seems inconsistent. I have seen some other stuff to the same effect. What I'm asking - is this behavior in the final C++11 standard, or is it just in a draft that was implemented early? If so, why did the standards committee include this behavior? It seems like it defeats the whole purpose of uniform initialization, as one has to remember which classes have initializer list constructors, and to use the old () syntax instead of {} with just those classes. Or one forgoes uniform initialization altogether.

This seems like a big "gotcha". But there might be advantages to it that I am not aware of.

Edit: this code:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a{2};
    for (auto x: a) {
        std::cout << x << std::endl;
    }
    return 0;
}

outputs "2" on gcc 4.6.2


回答1:


Yes, this behaviour is intended, according to §13.3.1.7 Initialization by list-initialization

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:

— Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.

— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

As to "the whole purpose of uniform intialization"... "Uniform initialization" is a marketing term, and not a very good description. The standard has all the usual forms of initialization plus list-initialization, but no "uniform initialization". List initialization is not meant to be the ultimate form of initialization, it's just another tool in the utility belt.




回答2:


Uniform initialization doesn't mean what you think it does. It was added to make initialization more uniform amongst the types in C++. The reasoning is this:

typedef struct dog_ {
   float height;
   int weight;
} dog;
int main() { 
    dog Spot = { 25.6, 45};
    dog Data[3] = { Spot, {6.5, 7} };
    std::array<dog, 2> data = { { Spot, {6.5, 7} } }; //only in C++ obviously
    return 0;
}

This is valid C and C++ code, and has been for many many years. It was really convenient, but you had to remember that this only worked with POD types. People have complained for a long time that there is no way to do std::vector<int> data = { 3, 7, 4, 1, 8};, but some classes (std::array) were written in weird ways to allow initializer list constructors.

So for C++11, the committee made it so that we could make vector and other cool classes do this too. This made construction of all types more uniform, so that we can use {} to initialize via constructors, and also from value lists. The problem you are running into, is that the constructor overload with an std::initializer_list<int> is the best match, and will be selected first. As such, std::vector<int> b{2}; does not mean call the constructor that takes an int, instead it means create a vector from this list of int values. In that light, it makes perfect sense that it would create a vector containing a single value of 2. To call a different constructor, you'll have to use the () syntax so C++ knows you don't want to initialize from a list.




回答3:


The standard states that the initializer list constructor takes precedence over the others. This is just one case where it isn't possible do just replace () with {}. There are others, for example {} initialization does not allow narrowing conversions.



来源:https://stackoverflow.com/questions/9723164/why-does-stdvectorint-b2-create-a-1-element-vector-and-not-a-2-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!