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聚合初始化来检测变窄的转换?
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)
它本身不会在我今天尝试的任何编译器上发出警告。
在使用 () 初始化时,您不应该想要“限制狭窄”。
此功能的主要目的是允许在转发场景中使用聚合,例如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 传递float给Agg. 这始终是缩小转换,因此始终是非法的。
列表初始化的收缩预防是有一定意义的,因为最好在本地使用支撑初始化列表。被初始化的类型是已知的,任何文字值都将在{}使用的地方直接提供。因此,有足够的信息以合理的方式处理缩小问题。
一旦你进入转发,这就是行不通的。缩小预防将剔除在本地可以正常使用的参数。
所以问题是:对于所有有效的 X、Y 和 Z,你想像emplace(X, Y, Z)以前一样工作Agg{X, Y, Z}吗?如果答案是肯定的,那么()聚合初始化不能防止缩小。