P0960,是否有某种机制可以检测c ++ 20中带有()的新聚合init的范围是否缩小?

san*_*orn 4 c++ c++-standard-library narrowing c++20

使用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
}
Run Code Online (Sandbox Code Playgroud)

是否可以使用带括号的C ++ 20聚合初始化来检测变窄的转换?

Bar*_*rry 7

Paren初始化聚合可以缩小转换范围。

构造函数和聚合初始化的行为不同,该功能看起来像构造函数的调用,因此,故意将其设计为尽可能构造函数的调用。在聚集初始化的情况下,聚集初始化的所有显着功能(缩小收敛范围,延长引用的生命周期等)都不是故意存在的。

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


特别:

 auto q = new Foo(INT_MAX, UINT_MAX); 
Run Code Online (Sandbox Code Playgroud)

几乎就像您实际编写了此构造函数一样:

struct Foo
{
  Foo(int x, int y) : x(x), y(y) { } // ~ish
  int x, y;
};
Run Code Online (Sandbox Code Playgroud)

它本身不会在我今天尝试的任何编译器上发出警告。

  • @sandthorn今天的操作方式相同-使用`{}`s (3认同)

Nic*_*las 5

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

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

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

struct Agg { int i; };

Agg a{5.0f};
Run Code Online (Sandbox Code Playgroud)

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

vector<Agg> v;
v.emplace_back(5.0f);
Run Code Online (Sandbox Code Playgroud)

编译器看到的只是emplace_back将尝试将 a 传递floatAgg. 这始终是缩小转换,因此始终是非法的。

列表初始化的收缩预防是有一定意义的,因为最好在本地使用支撑初始化列表。被初始化的类型是已知的,任何文字值都将在{}使用的地方直接提供。因此,有足够的信息以合理的方式处理缩小问题。

一旦你进入转发,这就是行不通的。缩小预防将剔除在本地可以正常使用的参数。

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