转发就地构造和列表初始化

时间:2015-04-10 15:17:43

标签: c++ c++11 language-lawyer object-construction list-initialization

通过转发就地构造,我的意思是std::allocator::construct和各种安抚方法,例如std::vector::emplace_back。我只是发现在C ++中转发的就地构造不能(无法?)利用列表初始化语法。结果,似乎人们永远无法就地构建聚合。我只是想确定转发的就地构造是否不支持列表初始化,因此也不支持聚合类型。这是由于语言的限制吗?有人可以提供有关此问题的标准吗?以下是一个例子:

虽然我们可以像

那样直接进行就地建设
int(*p)[3] = ...;
new(p) int[3]{1, 2, 3};

我们不能像

一样转发就地构建
std::allocator<int[3]> allo;
allo.construct(p, 1, 2, 3);

2 个答案:

答案 0 :(得分:5)

虽然{}被称为统一初始化语法,但它远非普遍。

采取以下两个例子:

size_t a = 3;
size_t b = 1;
std::vector<size_t> v1{a,b};
std::vector<size_t> v2(a,b);

在第一种情况下,我们构造了一个包含两个元素31的向量。

在第二种情况下,我们创建一个包含1,1,1的向量 - 1的3个副本。

live example

因此,基于{}的构造在某些情况下可能会导致与基于()的构造不同的行为。更重要的是,在上述情况下,没有办法达到1&#34;的副本。语法使用{}构造(我知道)。但是{3,2}案例可以通过简单地显式创建初始化列表并将其传递给()来处理。

由于大多数采用初始化列表的类型可以通过显式传入初始化列表来构建,并且C ++标准库是为具有构造函数的类型设计的,而不是没有它们的类型,C ++标准库几乎统一地使用{{{{{{ 1}}而不是()

缺点是想要进行列表初始化的类型不能通过这种机制进行安置。

理论上,使用{}构造的list_emplace方法可以添加到每个接口。我鼓励你提出这个建议!

答案 1 :(得分:3)

std::allocator的{​​{1}}(以及construct()提供的默认实现)指定使用std::allocator_traits()(请参阅[allocator.members] ] / p12,[allocator.traits.members] / p5)。

此时将其更改为::new((void *)p) U(std::forward<Args>(args)...)是不切实际的,因为它会默默地破坏现有代码:

{}

std::vector<std::vector<int>> foo; foo.emplace_back(10, 10); // add a vector of ten 10s with (); two 10s with {} 如果{}不起作用,可以an LWG issue使用()。我们必须看看委员会是否同意这个方向。

@Yakk指出了这种方法的潜在缺点:

foo.emplace_back(10); // ten 0s
foo.emplace_back(10, 10); // ten 10s
foo.emplace_back(10, 10, 10); // three(!) 10s

类似的问题(参见N2215的附录B)导致决定列表初始化总是更喜欢initializer_list构造函数。