Компиляция этого кода с помощью g ++ 4.2.1:
struct S { };
template<typename T> struct ST { };
template<typename BaseType>
class ref_count : private BaseType { };
template<typename RefCountType>
class rep_base : public RefCountType { };
class wrap_rep : public rep_base<ref_count<S> > {
typedef rep_base<ref_count<S> > base_type; // line 11
};
Я добираюсь:
bug.cpp:1: error: ‘struct S’ is inaccessible
bug.cpp:11: error: within this context
Однако, если я изменяюсь wrap_rep
класс для использования ST
:
class wrap_rep : public rep_base<ref_count< ST<int> > > {
typedef rep_base<ref_count< ST<int> > > base_type;
};
это компилирует прекрасный. С другой стороны, если я изменяю исходный код на:
class wrap_rep : public rep_base<ref_count<S> > {
typedef rep_base<ref_count< ::S > > base_type; // now using ::
};
это также компилирует прекрасный. Мне исходный код кажется прекрасным как есть. Действительно ли это - g ++ ошибка? В противном случае затем, почему делает использование шаблонной работы? И, для другого случая, почему ::S
необходимо?
Оба этих кода недействительны (действителен только последний), но ваш компилятор (который не соответствует) диагностирует только одно. Как говорится в другом ответе, здесь используется имя внедренного класса. Считается, что класс S
имеет имя члена S
, обозначающее тот же самый класс. Например (обратите внимание, что ключевое слово "class" перед S :: S
в первом примере необходимо для принудительной ссылки на внедренное имя класса вместо конструктора по умолчанию):
class S { };
class S::S object; // creates an S object
class X : S::S::S::S { }; // derives from class S
В шаблонах классов также есть введенное имя класса. Как и имя внедренного класса, оно наследуется производным классам, и поэтому ST
неправильно сформирован, потому что он использует это имя внедренного класса, которое, однако, недоступно. Если вы используете GCC меньше 4.5, это может иметь какое-то отношение к изменению , введенному в GCC4.5:
G ++ теперь реализует DR 176. Ранее G ++ не поддерживал использование injected-class-name из базовый класс шаблона в качестве имени типа, и поиск по имени нашел объявление шаблона во включающей области.Теперь поиск имени находит внедренное имя-класса, которое может использоваться либо как тип, либо как шаблон, в зависимости от того, следует ли за именем список аргументов шаблона или нет. В результате этого изменения некоторый код, который был ранее принят, может быть неправильно сформирован, потому что
- введенное имя-класса недоступно, потому что оно из частной базы, или
- Введенное-имя-класса не может быть используется в качестве аргумента для параметра шаблона шаблона.
В любом из этих случаев код можно исправить, добавив спецификатор вложенного имени для явного имени шаблона. Первый можно обойти с помощью -fno-access-control; второй отклоняется только с -pedantic.
Чтобы немного повеселиться с внедренными именами классов - обратите внимание, что внедренное имя класса не эквивалентно typedef, как можно было бы подумать вначале. Имя внедренного класса - это имя класса, но не классифицируется как имя typedef, что означает, что оно может быть скрыто по именам функций, объектов или перечислителей:
// valid, the data-member hides the injected class name
struct S { int S; };
Чтобы сослаться на имя внедренного класса, вы можете сказать class S :: S
(аналогично, в списке базовых классов имена, не являющиеся типами, игнорируются, поэтому вам не нужны особые меры предосторожности), но простой поиск по S :: S
будет ссылаться на элемент данных.
Ваша структура S
является базовым классом wrap_rep
, что означает, что она вводится в wrap_rep
, как если бы там был анонимный typedef.
Использование оператора ::
перед S
в вашем typedef скажет вашему компилятору использовать не S
, от которого вы наследуете, а S
в глобальном пространстве имен.
См. эту ссылку.
Исходный код отлично скомпилирован в обновлении Sun WorkShop 6 2 Компиляторы C ++ ». Это единственное, к чему у меня есть доступ в моем офисе. Можно попробовать любой другой компилятор, который у вас есть.