Я получил очень необычную ошибку от 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 ++?
Выполнение любого из следующих действий останавливает возникновение ошибки:
Test
в класс, в отличие от класса шаблона.TheCatDog::Dog::bark();
в CatDog::Dog::bark();
,CrazyHouse
класс и слияние его содержимого в Test
,CatDog
класс, сливая его содержимое в Test
и изменение TheCatDog
псевдоним, чтобы указать на Test
,Поиск имени по идентификатору CatDog
находки Test::CatDog
который объявлен private
, Доступ осуществляется с CrazyHouse
, который не является friend
из Test
, Поэтому это незаконный доступ к защищенному участнику.
Как отмечает @ sj0h, в C ++ 11 ваш пример становится действительным, потому что они решили расширить доступ к телам вложенных классов таким же образом, как и функции-члены.
C ++ 98:
Члены вложенного класса не имеют специального доступа ни к членам включающего класса, ни к классам или функциям, которые предоставили дружбу включающему классу; должны соблюдаться обычные правила доступа (пункт 11).
C ++ 11:
Вложенный класс является членом, и поэтому имеет те же права доступа, что и любой другой член.
(Участники имеют право доступа private
члены вмещающего класса.)
Однако это изменение, похоже, не реализовано в GCC даже в последней сборке версии 4.9. Таким образом, чтобы быть в безопасности, это не помешает добавить friend
декларация. Это должно идти после определение члена:
friend struct CrazyHouse;
Обратите внимание, что это не делает то же самое, что изменение C ++ 11, потому что friend
Корабль не транзитивен, тогда как доступ, предоставленный вложенным членством.
Поведение компиляторов можно считать неправильным или правильным в зависимости от того, о какой версии C ++ идет речь. Похоже, что поведение clang правильное, если мы говорим о C ++ 11, и неправильное, если мы говорим о C ++ 98.
Элемент stackoverflow Доступ к вложенным классам C ++ следует уточнить это.