为什么在删除一个函数时,在函数参数中使用指定初始值设定项的这段代码从不明确变为不编译?
考虑以下代码:
struct A{
int x;
int y;
};
struct B{
int y;
int x;
};
void func (A){
}
void func (B){
}
int main()
{
func({.y=1,.x=1});
}
出于某种原因,clang 和 gcc 都认为这段代码是不明确的,尽管已知指定初始值设定项中的顺序必须与 struct 中的顺序相匹配,尽管出于某种原因,clang 允许它并且只是发出警告:
ISO C++ 要求在声明顺序中指定字段指示符;字段 'y' 将在字段 'x' [-Wreorder-init-list] 之后初始化
现在有趣开始了:如果我注释掉func(B)代码,代码就会从模棱两可变成无法编译。
这就是我认为超级奇怪的地方。这背后有什么逻辑吗?
如果我的困惑的原因不清楚:
如果我们有两个func(A)和func(B)代码GCC和铛考虑func({.y=1,.x=1})暧昧,但如果我删除func(B)从源则给出了一个错误(或警告铛的情况下)。换句话说,我们通过删除 1 个选项从 2 个选项变为 0 个选项。
神箭
回答
规则在[over.ics.list]/2 中:
如果初始值设定项列表是指定初始值设定项列表,则仅当参数具有可根据聚合初始化规则 ([dcl.init.aggr]) 从初始值设定项列表初始化的聚合类型时,才可能进行转换,在在这种情况下,隐式转换序列是用户定义的转换序列,其第二个标准转换序列是身份转换。
[注1:聚合初始化不需要按指定顺序声明成员。如果在重载决议后,顺序与所选重载不匹配,则参数的初始化将是格式错误的 ([dcl.init.list])。
[示例1:
struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order ([dcl.init.list]) g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order ([dcl.init.list]) g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }—结束示例]
—尾注]
基本上,指示符必须命名成员的规则在 [dcl.init.aggr] 中(因此它对您是否可以形成转换序列很重要)但指示符必须按顺序排列的规则在 [dcl.init. list](所以它就像后来发生的要求一样,并且不影响是否可以形成转换序列 - 所以它不是“sfinae-friendly”)。
换句话说,这很好:
因为你不能根据聚合初始化的规则来初始化一个Bfrom {.a=1}。
但是您的示例(以及上面引用的示例)是模棱两可的,因为两者都满足聚合初始化规则但稍后失败。