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

C++ P0960,是否有任何类型的机制来检测新聚合init中是否有收缩,c+中的()s+;20?,c++,c++-standard-library,c++20,narrowing,C++,C++ Standard Library,C++20,Narrowing,使用“允许从带括号的值列表初始化聚合”,还可以使用()s执行聚合初始化 但是,此初始化允许缩小,而{}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_MA

使用“允许从带括号的值列表初始化聚合”,还可以使用
()
s执行聚合初始化

但是,此初始化允许缩小,而
{}
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
}
#包括
#包括
结构Foo
{
int x,y;
};
int main()
{
//auto p=new Foo{INT\u MAX,UINT\u MAX};//仍然不会编译
auto q=new Foo(INT_MAX,UINT_MAX);//c++20允许缩小聚合初始化
std::向量v;
//v.emplace_back(Foo{INT_MAX,UINT_MAX});//仍然不会编译
v、 emplace_back(INT_MAX,UINT_MAX);/c++20允许缩小聚合初始值
//进一步完善货运代理
}

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

聚合初始化允许缩小转换

构造函数和聚合初始化的行为不同,并且该功能看起来像构造函数调用,因此有意将其设计为尽可能像构造函数调用。聚合初始化的所有显著特征(缩小凸度、引用的生存期延长等)在paren初始化情况下并不存在

唯一的区别是paren初始化聚合时会从左到右计算表达式(而使用构造函数调用时,我们会对参数进行不确定的计算)


具体而言:

 auto q = new Foo(INT_MAX, UINT_MAX); 
将主要表现为您实际编写了此构造函数:

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

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

在使用()初始化时,不应该希望“限制狭窄”

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

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

struct Agg { int i; };

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

vector<Agg> v;
v.emplace_back(5.0f);
向量v; v、 后置炮台(5.0f); 编译器看到的只是
emplace\u back
将尝试向
Agg
的聚合初始化传递一个
float
。这始终是一个缩小的转换,因此始终是非法的

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

一旦你进入转发,这就不起作用了。缩小预防范围将剔除局部值良好的参数


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

您正在寻找编译器特定的答案吗?@p.W“Anything”我担心这个标准操作可能会由于不知道缩小而导致许多微妙的错误。更重要的是,完美的转发实际上是“无处不在”。如果一个人能在他或她感到不舒服的地方“限制”狭窄,那一定是一种解脱。这个例子真是致命!但是,我们如何限制“外部”上“Foo”初始化的狭窄性呢?@sandthorn与您今天所做的一样-使用
{}
我感觉像是有了
-frestrict-showing-paren-aggregates init
这样的东西。所以我不会错过我想念自己的地方。