【问题标题】:Forwarded in-place construction and list-initialization转发就地构造和列表初始化
【发布时间】:2023-03-12 18:54:01
【问题描述】:

通过转发就地构造,我指的是 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);

【问题讨论】:

  • 你有什么问题?
  • 类似Type object((foo, bar), anotherFoo, anotherBar);?
  • 我对这个问题的原始陈述不清楚。现已更新。
  • 您是否尝试过编写使用{} 的转发构造函数类型的东西?
  • std::allocator::construct(和std::allocator_traits::construct)被指定使用()而不是{},并且出于兼容性原因必须保持使用(vector&lt;int&gt;(10, 10)vector&lt;int&gt;{10, 10})。如果() 不起作用,可以使用an LWG issue 使其回退到使用{}

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


【解决方案1】:

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

举这两个例子:

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 的 3 个副本”语法。但是{3,2} 的情况可以通过简单地显式创建一个初始化列表并将其传递给() 来处理。

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

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

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

【讨论】:

  • 您还必须将list_construct 添加到分配器。我不确定我是否喜欢这个方向。
  • @T.C.我看到了另一个方向(如果括号构造失败,construct 将退回到列表构造中)。这似乎很容易出错:emplace(1,2) 放置 2emplace(1,2,3) 放置 1,2,3 似乎完全是精神错乱。
  • 这很好。我相信在 C++11 标准化过程中对列表初始化的语义进行了类似的讨论,导致“initializer_list 构造函数始终是{} 的首选”规则。
  • 我想根据提供的类型是否为聚合,在{}()之间自动切换是个好主意。
  • "使用 {} 构造没有办法达到“1 的 3 个副本”语法" std::vector&lt;int&gt;{3, 1, std::allocator&lt;int&gt;{}} 是在作弊吗? ;)
【解决方案2】:

std::allocatorconstruct()(也是std::allocator_traits提供的默认实现)被指定使用()::new((void *)p) U(std::forward&lt;Args&gt;(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 {}

如果() 不起作用,则有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 构造函数。

【讨论】:

  • 确实,(不知道这个问题),我实现了那个后备myself。它甚至不会破坏代码。
猜你喜欢
  • 2018-06-20
  • 2016-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多