通过转发就地构造,我认为std::allocater::construct和各种模板方法,例如std::vector::template_back。我只是发现C++中的转发就地构造并没有(不能?)利用列表初始化语法。因此,一个人似乎永远无法在原地构建一个集合。我只想确定转发的就地构造是否不支持列表初始化,因此不支持聚合类型。这是因为语言的限制吗?有人能提供关于这个问题的标准参考吗?下图:
虽然我们可以像一样直接进行现场施工
int(*p)[3] = ...;
new(p) int[3]{1, 2, 3};
我们不能像那样进行现场转发
std::allocator<int[3]> allo;
allo.construct(p, 1, 2, 3);
虽然{}
被称为统一的初始化语法,但它远不是通用的。
举两个例子:
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
-1
的3个副本的向量。
活生生的例子。
因此,在某些情况下,基于{}
的构造可能会导致与基于()
的构造不同的行为。此外,在上述情况下,使用{}
构造(据我所知)无法达到"1的3个副本"语法。但是{3,2}
的情况可以通过简单地显式创建初始值设定项列表并将其传递给()
来处理。
由于采用初始化器列表的大多数类型都可以通过显式传递初始化器列表来构造模板,并且C++标准库是为具有构造函数的类型而不是没有构造函数的类型设计的,因此C++标准库几乎一致地使用()
而非{}
来构造模板。
缺点是,无法通过此机制对想要列表初始化的类型进行模板设置。
理论上,可以将使用{}
构建的list_emplace
方法添加到每个接口。我鼓励你求婚!
std::allocator
的construct()
(以及std::allocator_traits
提供的默认实现)被指定为使用()
:::new((void *)p) U(std::forward<Args>(args)...)
(请参见[allocater.members]/p12,[allocateR.tracts.members]/p 5)
此时将其更改为{}
是不切实际的,因为它会悄无声息地破坏现有代码:
std::vector<std::vector<int>> foo;
foo.emplace_back(10, 10); // add a vector of ten 10s with (); two 10s with {}
存在一个LWG问题,如果()
不起作用,它将回退到使用{}
。我们得看看委员会是否同意这个方向。
@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
构造函数的决定。