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

时间:2012-03-15 15:42:30

标签: c++ c++11 initializer-list uniform-initialization

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

如果我想统一初始化int:

int a{5};

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

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

不构造二元素数组,而是构造一个值为2的元素。似乎需要更明确地了解它:

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

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

这似乎是一个很大的“陷阱”。但是我不知道它可能有优势。

编辑:此代码:

#include <iostream>
#include <vector>

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

在gcc 4.6.2上输出“2”

3 个答案:

答案 0 :(得分:22)

是的,根据§13.3.1.7按列表初始化初始化

,这种行为是有意的
  

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

     

- 最初,候选函数是初始化列表   类T的构造函数(8.5.4)和参数列表包含   初始化列表作为单个参数。

     

- 如果没有可行性   找到初始化列表构造函数,重载解析是   再次执行,其中候选函数都是   类T的构造函数和参数列表包含   初始化列表的元素。

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

答案 1 :(得分: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;
}

这是valid C and C++ code,而已经存在多年真的方便,但你必须记住,这只适用于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的{​​{1}},这是完全合理的。要调用不同的构造函数,您必须使用2语法,以便C ++知道您不想从列表初始化。

答案 2 :(得分:2)

标准规定初始化列表构造函数优先于其他构造函数。这只是一种情况,只能用()替换{}。还有其他一些,例如{}初始化不允许缩小转换次数。