I've been playing around with C++11 for the past few days, and I came up with something strange.
在过去的几天里,我一直在使用c++ 11,我发现了一些奇怪的东西。
If I want to uniformly initialize an int:
如果我想要均匀初始化一个int:
int a{5};
But if I do the same thing to a std::vector:
但是如果我对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:
不构造两个元素数组,而是构造一个值为2的元素的数组。似乎要达到这种效果,就需要更明确地说明:
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.
但不像b的声明,这看起来不一致。我还见过一些其他的东西也有同样的效果。我想问的是——这种行为是在最终的c++ 11标准中出现的,还是只是在早期实现的草案中出现的?如果是这样,为什么标准委员会会包括这种行为?它似乎违背了统一初始化的全部目的,因为必须记住哪些类具有初始化器列表构造函数,以及使用旧的()语法而不是仅仅使用这些类。或者完全放弃统一的初始化。
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
在gcc 4.6.2上输出“2”
3 个解决方案
#1
21
Yes, this behaviour is intended, according to §13.3.1.7 Initialization by list-initialization
是的,这种行为的目的是,根据§13.3.1.7 list-initialization初始化
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
当非聚合类类型T的对象初始化为list-initialized(8.5.4)时,重载解析分两个阶段选择构造函数:
— 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.-最初,候选函数是类T的initializer-list构造函数(8.5.4),而参数列表由初始化列表作为单个参数组成。
— 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.-如果没有找到可行的initializer-list构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,而参数列表由初始化器列表的元素组成。
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
6
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:
统一初始化并不意味着你所认为的那样。添加它是为了使c++中的类型之间的初始化更加统一。推理是这样的:
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.
这是有效的C和c++代码,已经存在多年了。这真的很方便,但是你必须记住这只适用于POD类型。长期以来,人们一直在抱怨无法实现std::vector
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.
对于c++ 11,委员会让我们可以让向量和其他很酷的类也这样做。这使得所有类型的构造更加统一,因此我们可以使用{}来通过构造函数和值列表初始化。您正在遇到的问题是,带有std::initializer_list
#3
2
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.
标准声明初始化器列表构造函数优先于其他构造函数。这只是一种不可能用{}替换()的情况。还有其他的,例如{}初始化不允许收缩转换。
#1
21
Yes, this behaviour is intended, according to §13.3.1.7 Initialization by list-initialization
是的,这种行为的目的是,根据§13.3.1.7 list-initialization初始化
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
当非聚合类类型T的对象初始化为list-initialized(8.5.4)时,重载解析分两个阶段选择构造函数:
— 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.-最初,候选函数是类T的initializer-list构造函数(8.5.4),而参数列表由初始化列表作为单个参数组成。
— 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.-如果没有找到可行的initializer-list构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,而参数列表由初始化器列表的元素组成。
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
6
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:
统一初始化并不意味着你所认为的那样。添加它是为了使c++中的类型之间的初始化更加统一。推理是这样的:
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.
这是有效的C和c++代码,已经存在多年了。这真的很方便,但是你必须记住这只适用于POD类型。长期以来,人们一直在抱怨无法实现std::vector
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.
对于c++ 11,委员会让我们可以让向量和其他很酷的类也这样做。这使得所有类型的构造更加统一,因此我们可以使用{}来通过构造函数和值列表初始化。您正在遇到的问题是,带有std::initializer_list
#3
2
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.
标准声明初始化器列表构造函数优先于其他构造函数。这只是一种不可能用{}替换()的情况。还有其他的,例如{}初始化不允许收缩转换。