27

过去几天我一直在玩 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”

4

3 回答 3

25

是的,这种行为是有意的,根据 §13.3.1.7 Initialization by list-initialization

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

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

— 如果没有找到可行的初始化列表构造函数,则再次执行重载决议,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成。

至于“统一初始化的整个目的”……“统一初始化”是一个营销术语,并不是一个很好的描述。该标准具有所有常见的初始化形式以及列表初始化,但没有“统一初始化”。列表初始化并不是初始化的终极形式,它只是实用工具带中的另一个工具。

于 2012-03-15T15:56:57.400 回答
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;
}

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

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

于 2012-03-15T16:47:37.560 回答
2

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

于 2012-03-15T15:54:12.890 回答