显然,不明确的调用不会在GCC上导致编译错误

Obviously ambiguous call does not cause a compilation error on GCC

本文关键字:GCC 错误 编译 不明确 调用 显然      更新时间:2023-10-16

我很惊讶GCC不认为以下程序中对foo()的调用不明确:

#include <iostream>
struct B1 { bool foo(bool) { return true; } };
struct B2 { bool foo(bool) { return false; } };
struct C : public B1, public B2
{
    using B1::foo;
    using B2::foo;
};
int main()
{
    C c;
    // Compiles and prints `true` on GCC 4.7.2 and GCC 4.8.0 (beta);
    // does not compile on Clang 3.2 and ICC 13.0.1;
    std::cout << std::boolalpha << c.foo(true);
}

上面的函数调用在GCC 4.7.2和GCC 4.8.0(测试版(上编译并返回true,而它在Clang 3.2和ICC 13.0.1上不会编译(正如我所料(。

这是">无需诊断"的情况,还是GCC中的错误鼓励参考C++11标准。

§7.3.3/3:

在用作成员声明的using声明中,嵌套名称说明符应命名所定义类的基类。如果这样的using声明命名构造函数,则嵌套名称说明符应命名所定义类的直接基类;否则它引入了通过成员名称查找(10.2,3.4.3.1(找到的声明集

¶14:

…[注意:两个using声明可能会引入具有相同名称和相同参数类型的函数。如果对不合格函数名称的调用,函数重载解析选择了由此类using声明引入的函数,则函数调用格式错误。

¶16:

为了解决过载问题,通过using声明引入到派生类将被视为派生类的成员。

因此,using声明是合法的,但正如您所说,函数是同一重载集中的对等函数,并且程序格式不正确。

正如您所说,程序中对foo(true)的调用显然是模棱两可的;此外,根据§10.2,因此,应在使用时对其进行标记。(标记using声明是不正确的;10.2(1(明确指出,名称的不明确使用是在查找时标记的,而不是在声明时标记的。(

将这个程序与类似的程序进行对比是很有趣的,类似的程序是一个公认的gcc错误的主题(从该错误报告中稍作修改,以使并行更清晰(:

#include <iostream>
struct A {
   static int foo() {return 1;}
   static int foo(char) { return 2;}
};
struct B1 : A {
//   using A::foo;
};
struct B2 : A {
//   using A::foo;
};
struct C : B1, B2 {
//   using B1::foo;
//   using B2::foo;
};
int main()
{
    std::cout << C::foo();
}

上述程序是正确的;尽管有钻石继承,但fooA的静态成员,因此它并不含糊。事实上,gcc编译它没有遇到任何麻烦。然而,取消对using A::foo的两个实例的注释(这不会改变任何一个foo(会导致gcc产生错误报告中记录的奇怪的重复错误。取消对C内的两个using声明的注释,这可能会触发本问题所涉及的另一个错误,然后屏蔽static function错误并使程序重新编译。

clang似乎处理了这个程序的所有可能的变体,不管它值多少钱。

最后,请注意,(在原始程序中(C中显式声明的foo(bool)将胜过using声明带入C范围的任何foo(bool)。我怀疑这两个错误都是错误记账的结果,同时试图跟踪每个类的作用域中的各种函数声明及其各自的出处(作为using声明和函数声明的序列(。