为什么'std :: vector <int> b {2};' 创建一个1元素的向量,而不是一个2元素的向量?

Rob*_*son 23 c++ initializer-list uniform-initialization c++11

过去几天我一直在玩C++ 11,我想出了一些奇怪的东西.

如果我想统一初始化一个int:

int a{5};
Run Code Online (Sandbox Code Playgroud)

但是如果我对std :: vector做同样的事情:

std::vector<int> b{2};
Run Code Online (Sandbox Code Playgroud)

不构造一个两元素数组,而是一个具有一个值为2的元素的数组.似乎要获得这种效果,需要更加明确它:

std::vector<int> c{{2}};
std::vector<int> d = {2};
Run Code Online (Sandbox Code Playgroud)

但不像b的声明 - 这似乎不一致.我已经看到了其他一些相同的效果.我要问的是 - 这是最终C++ 11标准中的这种行为,还是只是在早期实施的草案中?如果是这样,为什么标准委员会会包含这种行为?看起来它破坏了统一初始化的整个目的,因为必须记住哪些类具有初始化列表构造函数,并且只使用old()语法而不是{}.或者一个人放弃统一初始化.

这似乎是一个很大的"陷阱".但是我不知道它可能有优点.

编辑:此代码:

#include <iostream>
#include <vector>

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

在gcc 4.6.2上输出"2"

R. *_*des 22

是的,根据§13.3.1.7按列表初始化初始化,这种行为是有意的

当非聚合类类型T的对象被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:

- 最初,候选函数是类的初始化列表构造函数(8.5.4),T参数列表由初始化列表作为单个参数组成.

- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成.

至于"统一初始化的整个目的"......"统一初始化"是一个营销术语,并不是一个很好的描述.该标准具有所有常用的初始化形式列表初始化,但没有"统一初始化".列表初始化并不是初始化的最终形式,它只是实用程序带中的另一个工具.

  • @RobertMason:使用旧语法的"恼人的陷阱"给你的是"最令人烦恼的解析".对于想要在具有有能力的初始化列表的类上调用非初始化列表构造函数的少数和相对较小的情况,处理起来要困难得多.毕竟,你经常使用`vector <int>`?使用非整数(或浮点)类型的`vector`不会发生这种情况. (2认同)

Moo*_*uck 6

统一初始化并不意味着您的想法.添加它是为了使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;
}
Run Code Online (Sandbox Code Playgroud)

这是有效的C和C++代码,已经使用了很多年.这真的很方便,但你必须记住,这只适用于POD类型.人们长时间抱怨没有办法std::vector<int> data = { 3, 7, 4, 1, 8};,但有些类(std::array)以奇怪的方式编写,以允许初始化列表构造函数.

因此,对于C++ 11,委员会做了这样的事情,我们可以让矢量和其他很酷的类也这样做.这使得所有类型的构造更加统一,因此我们可以使用{}通过构造函数和值列表进行初始化.您遇到的问题是,构造函数重载std::initializer_list<int>是最佳匹配,并将首先选择.因此,std::vector<int> b{2};并不意味着调用构造函数int,而是意味着vector从这个int值列表中创建一个.从这个角度来看,完全意义上它会创建一个vector包含单个值的2.要调用不同的构造函数,您必须使用()语法,以便C++知道您不想从列表初始化.

  • "*对于构造函数来说,使用`{}`而不是`()`并不意味着傻.*"事实上,如果没有构造函数重载只接受`std ::这就是它的意思. initializer_list <>`实例. (3认同)
  • Stroustrup在他的常见问题解答http://www2.research.att.com/~bs/C++0xFAQ.html#uniform-init中提到统一初始化意味着_all initialization_.但也有例外(比如`std :: initializer_list`s),你必须使用"旧式". (2认同)
  • @Mooing:它存在于不同的上下文中,但是如果由于语法错误以前没有用于编译的东西现在在新标准下编译,我认为将新的合法语法称为"new"是安全的. (2认同)