使用P0960“允许从带括号的值列表中初始化聚合”,您也可以使用()初始化聚合。
然而,这个初始化允许缩小范围,而{}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聚合初始化来检测缩小转换范围?
发布于 2019-04-08 20:17:48
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;
};它本身不会对我今天尝试的任何编译器发出警告。
发布于 2019-04-11 01:21:55
在使用()初始化时,您不应该想要“限制范围”。
此功能的要点是允许在转发场景中使用聚合,例如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的聚合初始化。这始终是缩小转换,因此始终是非法的。
列表初始化的缩小预防是有意义的,因为带括号的init列表最好在本地使用。被初始化的类型是已知的,并且在使用{}的地方将直接提供任何文字值。因此,有足够的信息来以合理的方式处理缩小范围的问题。
一旦你进入转发阶段,这就不能正常工作了。缩小预防将剔除其值在本地应该是好的参数。
所以问题是这样的:对于所有有效的X、Y和Z,您希望emplace(X, Y, Z)像Agg{X, Y, Z}一样工作吗?如果答案是肯定的,则()聚合初始化无法阻止范围缩小。
https://stackoverflow.com/questions/55572101
复制相似问题