1

通过转发就地构造,我的意思是std::allocator::construct和各种 emplace 方法,例如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);
4

2 回答 2

5

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

举两个例子:

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

在第一种情况下,我们构造一个包含两个元素的向量,3并且1

在第二种情况下,我们创建一个包含1,1,1- 3 个1.

活生生的例子

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

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

缺点是无法通过此机制放置想要进行列表初始化的类型。

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

于 2015-04-10T16:17:54.060 回答
3

std::allocatorconstruct()(以及由 提供的默认实现std::allocator_traits)被指定为使用():(::new((void *)p) U(std::forward<Args>(args)...)参见 [allocator.members]/p12, [allocator.traits.members]/p5)。

此时将其更改为不切实际{},因为它会默默地破坏现有代码:

std::vector<std::vector<int>> foo;
foo.emplace_back(10, 10); // add a vector of ten 10s with (); two 10s with {}

存在一个 LWG 问题,使其回退到使用{}if()不起作用。我们必须看看委员会是否同意这个方向。

@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构造函数的决定。

于 2015-04-10T16:20:37.230 回答