10

令我惊讶的是,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);
}

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

这是“不需要诊断”的情况,还是 GCC 中的错误?鼓励参考 C++11 标准。

4

2 回答 2

4

§7.3.3/3:

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

¶14:

... [注意:两个 using 声明可能会引入具有相同名称和相同参数类型的函数。如果对于非限定函数名的调用,函数重载决议选择了由这种 using 声明引入的函数,则该函数调用格式错误。

¶16:

出于重载决议的目的,通过 using 声明引入派生类的函数将被视为派生类的成员。

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

于 2013-02-26T03:41:42.593 回答
1

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();
}

上述程序是正确的;尽管菱形继承,foo是 的静态成员A,所以它不是模棱两可的。事实上,gcc 编译它没有问题。但是,取消注释 的两个实例(using A::foo这不会改变任何一个foo)会导致 gcc 产生错误报告中指出的奇怪的重复错误。取消注释里面的两个using声明C,这可能会触发另一个错误,这是这个问题的主题,然后掩盖static function错误并导致程序再次编译。

clang 似乎可以处理该程序的所有可能变体,因为它的价值。

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

于 2013-02-26T02:05:53.393 回答