P0960,是否有任何类型的机制来检测新的聚合init是否在c ++ 20中使用()s缩小?

问题描述 投票:4回答:2

使用P0960“允许从带括号的值列表中初始化聚合”,您也可以使用()s进行聚合init。

但是,这种初始化允许缩小,而{}s则不然。

#include <vector>
#include <climits>

struct Foo
{
  int x, y;
};

int main()
{
  // auto p = new Foo{INT_MAX, UINT_MAX}; // still won't compile
  auto q = new Foo(INT_MAX, UINT_MAX);    // c++20 allows narrowing aggregates init

  std::vector<Foo> v;
  // v.emplace_back(Foo{INT_MAX, UINT_MAX}); // still won't compile
  v.emplace_back(INT_MAX, UINT_MAX);         // c++20 allows narrowing aggregates init
                                             // in furtherly perfect forwardings
}

是否可以通过括号使用C ++ 20聚合初始化来检测缩小转换?

c++ c++-standard-library c++20 narrowing
2个回答
7
投票

Paren初始化聚合允许缩小转换。

构造函数和聚合初始化的行为不同,并且该特性看起来像构造函数调用,因此它被有意设计为尽可能像构造函数调用。在paren-initialization案例中,聚合初始化的所有显着特征(缩小的转换,引用的生命周期扩展等)非常有意地不存在。

唯一的区别是paren-initializing聚合确实从左到右评估表达式(而对于构造函数调用,我们对参数进行了不确定的评估)。


特别:

 auto q = new Foo(INT_MAX, UINT_MAX); 

将表现得好像你实际上已经编写了这个构造函数:

struct Foo
{
  Foo(int x, int y) : x(x), y(y) { } // ~ish
  int x, y;
};

哪个本身不会对我今天尝试的任何编译器发出警告。


4
投票

使用()初始化时,您不应该“限制狭窄”。

此功能的要点是允许在转发方案中使用聚合,例如container::emplace,就地构造函数等。它们目前不起作用,因为std::allocator_traits<>::construct不会也不能使用列表初始化语法,因为有太多的情况会隐藏你可能想要调用的构造函数。

在转发方案中,在处理聚合时,准确剔除缩小转换的能力是有限的。为什么?考虑一下:

struct Agg { int i; };

Agg a{5.0f};

这不是一个缩小的转换,因为特定的浮点字面值可以转换为int而不会损失精度。但是,当您通过emplace等转发构造时,编译器无法看到参数的实际值。它只看到类型。所以,如果你这样做:

vector<Agg> v;
v.emplace_back(5.0f);

所有编译器都看到emplace_back将尝试将float传递给Agg的聚合初始化。这总是一个缩小的转换,因此总是非法的。

列表初始化的缩小预防是有道理的,因为braced-init-lists最好在本地使用。正在初始化的类型是已知的,任何文字值都将直接在使用{}的地方提供。因此,有足够的信息以合理的方式处理缩小问题。

一旦你进入转发,这只是不起作用。缩小预防将剔除参数,这些参数的值在本地会很好。

所以问题是这样的:对于所有有效的X,Y和Z,你想要emplace(X, Y, Z)Agg{X, Y, Z}一样工作吗?如果答案是肯定的,那么()聚合初始化不能阻止缩小。

© www.soinside.com 2019 - 2024. All rights reserved.