Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.
like image 898
Ambroz Bizjak Avatar asked Feb 28 '14 00:02

Ambroz Bizjak


1 Answers

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 friendship is not transitive whereas access granted by nested membership is.

like image 173
Potatoswatter Avatar answered Sep 28 '22 20:09

Potatoswatter