[英]Why can't I use an arbitrary nesting of braces to construct most classes?
Given the following code:给定以下代码:
struct A;
struct B {
B() {}
B(A &&) {}
};
struct A {
A() {}
A(B &&) {}
};
Then I can use as many braces as I want to construct A
or B
.然后我可以使用尽可能多的大括号构造
A
或B
。
// default construct A
auto a = A{};
// default construct B, forward to A
auto b = A{{}};
// default construct A, forward to B, forward to A
auto c = A{{{}}};
// etc.
auto d = A{{{{}}}};
auto e = A{{{{{}}}}};
Similarly, given同样,给定
struct C {
C(std::initializer_list<C>) {}
};
then I can also use as many braces as I want然后我也可以使用任意多的牙套
// default construct C
auto f = C{};
// construct from initializer_list of one default constructed C
auto g = C{{}};
// construct from initializer_list of one C constructed from empty initializer_list
auto h = C{{{}}};
// etc.
auto i = C{{{{}}}};
auto j = C{{{{{}}}}};
Why doesn't the same argument work for a truly boring type?为什么同样的论点不适用于真正无聊的类型?
struct D {
};
or, rewritten for clarity:或者,为清楚起见重写:
struct D {
D() {}
D(D &&) {}
};
This fails even on这甚至失败了
auto k = D{{}};
Why does this not default construct a D
with the innermost braces, and then pass that rvalue on to the move constructor of D
?为什么这不默认用最里面的大括号构造一个
D
,然后将该右值传递给D
的移动构造函数?
See it live: https://godbolt.org/z/E763EPGh1现场观看:https://godbolt.org/z/E763EPGh1
There's a special case that precludes D{{}}
.有一种特殊情况排除了
D{{}}
。 It's a very particular set of conditions, so I imagine it's there specifically to prevent this exact recursion.这是一组非常特殊的条件,所以我想它是专门用来防止这种精确递归的。
[over.best.ics]/4 However, if the target is
[over.best.ics]/4但是,如果目标是
(4.1) — the first parameter of a constructor(4.1) — 构造函数的第一个参数
......
and the constructor... is a candidate by并且构造函数...是候选人
......
(4.5) — the second phase of [over.match.list] when the initializer list has exactly one element that is itself an initializer list, and the target is the first parameter of a constructor of classX
, and the conversion is toX
or reference to cvX
,(4.5) — [over.match.list]的第二阶段,当初始化列表恰好有一个元素本身就是初始化列表,并且目标是 class
X
的构造函数的第一个参数,并且转换为X
或参考cvX
,
user-defined conversion sequences are not considered.不考虑用户定义的转换序列。
D{{}}
is a list-initialization. D{{}}
是一个列表初始化。 D(D&&)
constructor is considered by the second phase of it (the first phase looks at initializer-list constructors, like C(std::initializer_list<C>)
in your second example). D(D&&)
构造函数由它的第二阶段考虑(第一阶段查看初始化列表构造函数,如第二个示例中的C(std::initializer_list<C>)
)。 But for it to be viable, there needs to be an implicit conversion from {}
to D&&
, and [over.best.ics]/4 suppresses it.但要使其可行,需要从
{}
到D&&
的隐式转换,并且[over.best.ics]/4会抑制它。
Your two versions of D
is in fact unequal.你的两个版本的
D
实际上是不相等的。 Suppose you have假设你有
struct E {};
struct F {
F() {}
F(F&&) {}
};
auto e = E{{}};
auto f = F{{}};
e
fails because E
is an aggregate . e
失败,因为E
是一个聚合。 Each comma separated element within the outermost {}
is used to initialize a member in E
.最外层
{}
中的每个逗号分隔元素都用于初始化E
中的成员。 Since E
contains nothing, you have too many initializers for E
.由于
E
不包含任何内容,因此E
的初始化程序太多。
As @IgorTandetnik mentioned, f
fails specifically because it is an exception in implicit conversions: a nested initializer list cannot be used to initialize the reference in copy or move constructors.正如@IgorTandetnik 提到的,
f
特别失败,因为它是隐式转换中的一个例外:嵌套的初始化列表不能用于初始化复制或移动构造函数中的引用。 This is true even if the inner initializer list contains more elements即使内部初始化列表包含更多元素也是如此
struct G {
G() {}
G(G&&) {}
G(int, int) {}
};
struct H {
H() {}
H(G&&) {}
};
auto g = G{{42, 420}}; // also fails
auto h = H{{42, 420}}; // ok
Presumably, this is to prevent infinite recursions想必这是为了防止无限递归
These rules prevent more than one user-defined conversion from being applied during overload resolution, thereby avoiding infinite recursion.
这些规则防止在重载解析期间应用多个用户定义的转换,从而避免无限递归。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.