C++ 使用;使用;两次由不同的编译器进行不同的解释

C++ 使用;使用;两次由不同的编译器进行不同的解释,c++,c++11,C++,C++11,考虑以下代码: struct A { int propose(); }; struct A1 : A { int propose(int); using A::propose; }; struct B1 : A1 { protected: using A1::propose; public: using A::propose; }; int main() { B1().propose(); } 让我们编译一下:g++-std=c++11 m

考虑以下代码:

struct A {
    int propose();
};

struct A1 : A {
    int propose(int);
    using A::propose;
};

struct B1 : A1 {
protected:
    using A1::propose;
public:
    using A::propose;
};

int main() {
    B1().propose();
}
让我们编译一下:
g++-std=c++11 main.cpp

我在使用GNU 4.8.1时遇到以下编译器错误:

main.cpp: In function 'int main()':                                                                                                                    
main.cpp:2:9: error: 'int A::propose()' is inaccessible
     int propose();
         ^
main.cpp:18:18: error: within this context
     B1().propose();
但是,此代码在AppleClang 6.0.0.6000056中编译


我知道没有必要在
B1
中使用
,(在我的代码中是必要的,但我有一个
错误地使用了太多的
)。无论如何,为什么Clang会编译它?这是预期的吗?

在[namespace.udecl]中,我们有:

当using声明将名称从基类带入派生类作用域时,成员函数和 派生类中的成员函数模板重写和/或隐藏成员函数和成员函数 具有相同名称、参数类型列表(8.3.5)、cv限定和参考限定符(如有)的模板 基类(而不是冲突的)

标准明确规定引入的名称不会与基类中的名称冲突。但它并没有提到引入相互冲突的名字

该节还说:

using声明是一种声明,因此可以在多个(且仅在多个)位置重复使用 允许声明。[示例:

-[结束示例]

有趣的是,在下面的示例中,GCC很乐意编译它(并打印A),而Clang允许构造C,但拒绝调用foo,因为它不明确:

struct A {
    void foo() { std::cout << "A\n"; }
};

struct B {
    void foo() { std::cout << "B\n"; }
};

struct C : A, B {
    using A::foo;
    using B::foo;
};


int main()
{
    C{}.foo();
    return 0;
}
结构A{
void foo(){std::cout声明是合法的

调用它是合法的,应该可以在任何地方使用,并且只能从类和派生类中调用,并且可以从任何类中调用。您会注意到,这没有什么意义

没有规则禁止在声明中构造(从具有相同签名的两个不同基类中导入两次名称),甚至在派生类进入并在导入后隐藏名称的“实”代码中使用

如果您不隐藏它,您将处于一种奇怪的情况,即相同的函数
A::propose
同时受保护和公开,因为它在同一范围内两次(合法)命名,具有不同的访问控制。这是不寻常的

如果您在一个类中,则子条款规定您可以使用它:

[class.access.base]/5.1

如果-(5.1)m作为N的成员是公共的,则当在类N中命名时,可以在点R处访问成员m

而且,
提议
显然是公开的。(它也是
受保护的
,但我们不必一直阅读该案例!)

在其他地方,我们有一个矛盾。你被告知你可以在任何地方不受限制地使用它[class.access]/1(3)。你被告知你只能在某些情况下使用它[class.access]/1(2)

我不确定如何解决这一模棱两可的问题


逻辑序列的其余部分:

在[namespace.udecl]/10中,我们有:

使用声明是一种声明,因此可以在允许(且仅限于)多个声明的情况下重复使用

和[namespace.udecl]/13:

由于using声明是一个声明,因此对同一声明区域中同名声明的限制

因此,使用X::propose;
的每个
都是声明

[basic.scope]对一个作用域中同名的两个函数没有适用的限制,但[basic.scope.class]/1(3)除外,它指出,如果声明的重新排序改变了程序,则该程序是格式错误的。因此,我们不能说后一个程序获胜

在[basic.scope]下,同一作用域中的两个成员函数声明是合法的。但是,在[over]下,对同名的两个成员函数有限制

[超过]/1说明:

当为同一作用域中的单个名称指定两个或多个不同的声明时,该名称被称为重载

过载也有一些限制。这通常是防止过载的原因

struct foo {
  int method();
  int method();
};
不合法。但是:

[过载]/1表示:

并非所有函数声明都可以重载。此处指定了不能重载的函数声明。如果一个程序在同一范围内包含两个这样的不可重载声明,则该程序是格式错误的。[注意:这是 限制适用于范围内的显式声明,以及此类声明和通过使用声明(7.3.3)进行的声明之间的限制。它不适用于由于名称查找(例如,由于使用指令)或重载解析(例如,对于运算符函数)而制造的函数集。-结束注释

注释明确允许重载限制考虑通过两个using声明引入的符号!规则仅适用于范围内的两个显式声明,或范围内的显式声明和using声明之间的两个显式声明

对两个使用声明没有任何限制。它们可以具有相同的名称,并且它们的签名可以冲突到您想要的程度

这是很有用的,因为通常您可以转到并隐藏它们的声明(在派生类中使用声明),并且不会出错[namespace.udecl]/15:

当using声明将基类中的名称引入派生类作用域时,派生类中的成员函数和成员函数模板将覆盖和/或隐藏基类中具有相同名称、参数类型列表(8.3.5)、cv限定符和ref限定符(如果有)的成员函数和成员函数模板(而不是相互冲突)

但是,这里没有这样做。然后我们调用该方法。发生重载解析

请参见[namespace.udecl]/16:

为了解决过载问题,由 在派生类中使用声明将被视为是der的成员
struct foo {
  int method();
  int method();
};
protected:
  int A::propose(); // X
  int A1::propose(int); // Y
public:
  int A::propose(); // Z