Ошибочный частный базовый класс недоступен?
Компиляция этого кода с использованием 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
необходимо?
3 ответа
Оба этих кода недействительны (допустим только последний), но ваш компилятор (который не соответствует) только диагностирует один. Как говорит другой ответ, для этого используется введенное имя класса. Класс S
считается, что имеет имя члена S
обозначает тот же класс. Например (обратите внимание на ключевое слово "класс" перед S::S
в первом примере необходимо принудительно указать ссылку на введенное имя класса вместо конструктора по умолчанию):
class S { };
class S::S object; // creates an S object
class X : S::S::S::S { }; // derives from class S
Шаблоны классов также имеют введенное имя класса. Как и введенное имя класса, оно наследуется от производных классов и, таким образом, ST<int>
неправильно сформирован, потому что использует это имя класса для инъекций, которое, однако, недоступно. Если вы используете GCC меньше 4.5, это может быть связано с изменением, внесенным в GCC4.5:
G++ теперь реализует DR 176. Раньше G++ не поддерживал использование injected-class-name базового класса шаблона в качестве имени типа, и поиск имени нашел объявление шаблона во вложенной области видимости. Теперь поиск имени находит имя введенного класса, которое можно использовать как тип или как шаблон, в зависимости от того, следует ли за именем список аргументов шаблона. В результате этого изменения ранее принятый код может быть неверно сформирован, поскольку
- Имя введенного класса недоступно, потому что это из частной базы, или
- Введенное имя класса не может использоваться в качестве аргумента для параметра шаблона шаблона.
В любом из этих случаев код можно исправить, добавив спецификатор nested-name для явного имени шаблона. Первый можно обойти с помощью -fno-access-control; вторая отклоняется только с -pedantic.
Чтобы получить больше удовольствия от введенных имен классов - обратите внимание, что введенное имя класса не эквивалентно 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 update 2 Compilers C++". Это единственный, к которому у меня есть доступ в моем офисе. Может быть, попробуйте любой другой компилятор у вас есть.