C++: Strange "is private" error

I've been getting a very unusual error from g++ claiming that a type alias is private. After hours of reducing my code, I've arrived at the following minimal test case:

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

The error with g++ 4.8.2 is:

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 accepts the same code. What's going on here, is this a g++ bug?

Doing any of the following stops the error from occuring:

  • Turning Test into a class, as opposed to a template class.
  • Removing any of the statements in any of the functions.
  • Changing TheCatDog::Dog::bark(); to CatDog::Dog::bark(); .
  • Removing the CrazyHouse class and merging its contents in Test .
  • Removing the CatDog class, merging its contents into Test and changing the TheCatDog alias to point to Test .

  • Name lookup on the identifier CatDog finds Test::CatDog which is declared private . The access is performed from CrazyHouse , which is not a friend of Test . Therefore it's an illegal access to a protected member.

    As @sj0h notes, in C++11 your example becomes valid because they decided to extend access to the bodies of nested classes in the same way as member functions.

    C++98:

    The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed.

    C++11:

    A nested class is a member and as such has the same access rights as any other member.

    (Members have the right to access private members of the enclosing class.)

    However, this change does not appear to be implemented in GCC even in a recent build of version 4.9. So, to be safe, it can't hurt to add a friend declaration. This must go after the definition of the member:

    friend struct CrazyHouse;
    

    Note that this doesn't accomplish exactly the same thing as the C++11 change, because friend ship is not transitive whereas access granted by nested membership is.


    The behavior of the compilers can be considered wrong or right depending on which version of C++ we are talking about. It appears that the behavior of clang is correct if we are talking about C++11 and incorrect if we are talking about C++98.

    The stackoverflow item C++ nested class access should clarify that.

    链接地址: http://www.djcxy.com/p/77718.html

    上一篇: 覆盖原型属性或功能

    下一篇: C ++:奇怪的是“私人”的错误