C ++:奇怪的“私有”错误

时间:2014-02-28 00:19:45

标签: c++ class private compiler-bug g++4.8

我从g ++那里得到了一个非常不寻常的错误,声称类型别名是私有的。经过几个小时的减少我的代码,我已经达到了以下最小的测试用例:

template <typename Dummy>
class Test {
    struct CatDog {
        static void meow ()
        {
            CrazyHouse::TheCatDog::meow();
        }

        struct Dog {
            static void bark ();
        };
    };

    struct CrazyHouse {
        using TheCatDog = CatDog;

        static void startMadness ()
        {
            TheCatDog::meow();
            TheCatDog::Dog::bark();
        }
    };

public:
    static void init ()
    {
        CrazyHouse::startMadness();
    }
};

int main ()
{
    Test<void> t;
    t.init();
}

g ++ 4.8.2的错误是:

test.cpp: In instantiation of 'static void Test<Dummy>::CatDog::meow() [with Dummy = void]':
test.cpp:19:29:   required from 'static void Test<Dummy>::CrazyHouse::startMadness() [with Dummy = void]'
test.cpp:27:34:   required from 'static void Test<Dummy>::init() [with Dummy = void]'
test.cpp:34:12:   required from here
test.cpp:15:33: error: 'using TheCatDog = struct Test<void>::CatDog' is private
         using TheCatDog = CatDog;
                                 ^
test.cpp:6:41: error: within this context
             CrazyHouse::TheCatDog::meow();
                                         ^

Clang 3.4接受相同的代码。这里发生了什么,这是一个g ++ bug?

执行以下任何操作可以阻止错误发生:

  • Test转换为类,而不是模板类。
  • 删除任何功能中的任何陈述。
  • TheCatDog::Dog::bark();更改为CatDog::Dog::bark();
  • 删除CrazyHouse类并将其内容合并到Test
  • 删除CatDog类,将其内容合并到Test并将TheCatDog别名更改为指向Test

2 个答案:

答案 0 :(得分:5)

标识符CatDog上的名称查找找到Test::CatDog,其声明为private。访问权限来自CrazyHouse,而friend不是Test。因此,这是对受保护成员的非法访问。

正如@ sj0h所指出的,在C ++ 11中,您的示例变得有效,因为他们决定以与成员函数相同的方式扩展对嵌套类主体的访问。

C ++ 98:

  

嵌套类的成员对封闭类的成员没有特殊访问权限,也没有对已经为封闭类授予友谊的类或函数;应遵守通常的准入规则(第11条)。

C ++ 11:

  

嵌套类是成员,因此具有与任何其他成员相同的访问权限。

(会员有权访问附上课程的private名成员。)

但是,即使在4.9版本的最新版本中,此更改似乎也未在GCC中实现。因此,为了安全起见,添加friend声明不会有什么坏处。 成员的定义之后必须

friend struct CrazyHouse;

请注意,这与C ++ 11更改完全不同,因为friend发送不可传递,而嵌套成员资格授予的访问权限是。

答案 1 :(得分:-1)

根据我们所讨论的C ++版本,编译器的行为可能被视为错误或正确。如果我们讨论的是C ++ 11,那么clang的行为似乎是正确的,如果我们谈论的是C ++ 98,那就是错误的。

stackoverflow项C++ nested class access应该澄清这一点。