微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么在删除一个函数时,在函数参数中使用指定初始值设定项的这段代码从不明确变为不编译?

如何解决为什么在删除一个函数时,在函数参数中使用指定初始值设定项的这段代码从不明确变为不编译?

考虑以下代码

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 都认为这段代码不明确,即使已知指定初始化程序中的顺序必须与结构中的顺序相匹配,尽管出于某种原因,clang 允许它并只是发出警告:

ISO C++ 要求在声明中指定字段指示符 命令;字段 'y' 将在字段 'x' 之后初始化 [-Wreorder-init-list]

现在好戏开始了: 如果我将 func(B) 注释掉,代码就会从模棱两可变成无法编译。

这就是我认为超级奇怪的地方。这背后有什么逻辑吗?

如果我的困惑的原因不清楚:

如果我们在代码中同时有 func(A)func(B),gcc 和 clang 都认为 func({.y=1,.x=1}) 不明确,但是如果我从源中删除 func(B) 那么它会给出一个错误(或在叮当声的情况下发出警告)。换句话说,我们通过删除 1 个选项从 2 个选项变为 0 个选项。

godbolt

解决方法

规则在 [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
}

——结束示例]

——尾注]

基本上,指示符必须命名成员的规则在 [dcl.init.aggr] 中(因此它对您是否可以形成转换序列很重要)但指示符必须按顺序排列的规则在 [dcl .init.list](所以它就像后面发生的要求一样,不影响是否可以形成转换序列 - 所以它不是“sfinae-friendly”)。

换句话说,这很好:

struct A { int a; };
struct B { int b; };
​
void f(A);
void f(B);
​
void call_f() {
    f({.a=1}); // ok,calls f(A)
}

因为您无法根据聚合初始化规则从 B 初始化 {.a=1}

但是您的示例(以及上面引用的示例)是模棱两可的,因为两者都满足聚合初始化规则但稍后失败。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。