Видимость частных унаследованных typedefs для вложенных классов
в следующем примере (извинения за длину) я попытался изолировать некоторое неожиданное поведение, с которым я столкнулся при использовании вложенных классов в классе, который в частном порядке наследуется от другого. Я часто видел утверждения о том, что нет ничего особенного во вложенном классе по сравнению с ненастным классом, но в этом примере можно видеть, что вложенный класс (по крайней мере, в соответствии с GCC 4.4) может видеть открытые typedefs класса, который приватно наследуется закрытием класс.
Я ценю, что typdefs не то же самое, что данные членов, но я нашел это поведение удивительным, и я думаю, что многие другие тоже. Поэтому мой вопрос двоякий:--2-->
- это стандартное поведение? (достойное объяснение, почему было бы очень полезно)
- можно ли ожидать, что он будет работать на большинстве современных компиляторов (т. е. насколько он портативен)?
#include <iostream>
class Base {
typedef int priv_t;
priv_t priv;
public:
typedef int pub_t;
pub_t pub;
Base() : priv(0), pub(1) {}
};
class PubDerived : public Base {
public:
// Not allowed since Base::priv is private
// void foo() {std::cout << priv << "n";}
class Nested {
// Not allowed since Nested has no access to PubDerived member data
// void foo() {std::cout << pub << "n";}
// Not allowed since typedef Base::priv_t is private
// void bar() {priv_t x=0; std::cout << x << "n";}
};
};
class PrivDerived : private Base {
public:
// Allowed since Base::pub is public
void foo() {std::cout << pub << "n";}
class Nested {
public:
// Works (gcc 4.4 - see below)
void fred() {pub_t x=0; std::cout << x << "n";}
};
};
int main() {
// Not allowed since typedef Base::priv_t private
// std::cout << PubDerived::priv_t(0) << "n";
// Allowed since typedef Base::pub_t is inaccessible
std::cout << PubDerived::pub_t(0) << "n"; // Prints 0
// Not allowed since typedef Base::pub_t is inaccessible
//std::cout << PrivDerived::pub_t(0) << "n";
// Works (gcc 4.4)
PrivDerived::Nested o;
o.fred(); // Prints 0
return 0;
}
5 ответов
предисловие: в ответе ниже я ссылаюсь на некоторые различия между C++98 и c++03. Однако оказывается, что изменения, о которых я говорю, еще не вошли в стандарт, поэтому C++03 В этом отношении не отличается от C++98 (спасибо Йоханнесу за указание на это). Почему-то я был уверен, что видел его в C++03, но на самом деле его там нет. Тем не менее, проблема действительно существует (см. ссылку DR в комментарии Johannes), и некоторые компиляторы уже реализуют то, что они вероятно, рассмотрим наиболее разумное решение этого вопроса. Таким образом, ссылки на C++03 В приведенном ниже тексте неверны. Пожалуйста, интерпретируйте ссылки на C++03 Как ссылки на некоторые гипотетические, но очень вероятные будущие спецификации этого поведения, которые некоторые компиляторы уже пытаются реализовать.
важно отметить, что между стандартами C++98 и C++03 произошло значительное изменение прав доступа для вложенных классов.
В C++98 вложенный класс не имели особых прав доступа к членам объемлющего класса. Это был в основном полностью независимый класс, только что объявленный в рамках закрытого класса. Он мог только получить доступ общественные члены включающего класса.
В C++03 вложенному классу были предоставлены права доступа к членам заключительного класса как включающего класса. Точнее, вложенному классу были предоставлены те же права доступа как статический член функция включающего класса. Т. е. теперь вложенный класс может получить доступ к любой члены заключительного класса, включая частная те.
по этой причине вы можете наблюдать различия между различными компиляторами и версиями одного и того же компилятора в зависимости от того, когда они реализовали новую спецификацию.
конечно, вы должны помнить, что объект вложенного класса не привязаны к каким-либо конкретным объектом включающий класс. Что касается реальных объектов, то это два независимых класса. Для доступа к нестатическим элементам данных или методам заключающего класса из вложенного класса необходимо иметь определенный объект заключающего класса. Другими словами, снова вложенный класс действительно ведет себя так же, как статическая функция-член объемлющего класса: он не имеет конкретного this
указатель для включающего класса, поэтому он не может получить доступ к нестатическим члены заключительного класса, если вы не приложите усилий, чтобы предоставить ему доступ к определенному объекту заключительного класса. Без него вложенный класс может получить доступ только к typedef-именам, перечислениям и статическим членам заключающего класса.
простой пример, иллюстрирующий разницу между C++98 и c++03, может выглядеть следующим образом
class E {
enum Foo { A };
public:
enum Bar { B };
class I {
Foo i; // OK in C++03, error in C++98
Bar j; // OK in C++03, OK in C++98
};
};
это изменение именно то, что позволяет ваш
короткий ответ: вложенные классы имеют доступ к содержащим классам private member в C++0x, но не C++1998 и c++2003. Однако это legal для компиляторов C++98 и c++2003 поддерживается поведение C++0x, так как старое поведение считается дефектом.
в разделе 11.8.1 стандартов C++98 и 2003 указано:
члены вложенного класса не имеют специальный доступ к членам включающего класса или классов или функции что даровали дружбу к заключительному классу; обычный правила доступа (статья 11) повиновался. Члены прилагающего класс не имеет специального доступа к члены вложенного класса; обычный правила доступа (статья 11) повиновался.
C++0x раздел 11.8.1 гласит:
вложенный класс является членом и как таковой имеет те же права доступа, что и любой другой участник. Члены объемлющего класса не имеют специального доступа к членам вложенного класса; обычные правила доступа (Статья 11) будьте послушны.
Отчет О Дефекте Основного Языка 45 показывает, что исходное поведение считалось дефектом, поэтому законно, чтобы компиляторы, отличные от C++0x, также поддерживали новое поведение, хотя это и не требуется.
на:
9.2 членов класса
1 [...] Члены класса данные-члены, функции-члены (9.3), вложенные типы, и перечислители. Данные-члены и функции-члены, статические и нестатические; см. 9.4.Вложенные типы классы (9.1, 9.7) и перечисления (7.2), определенные в классе, и произвольные типы, объявленные в качестве членов с помощью декларации typedef (7.1.3).
для отвечайте на вопросы:
- это стандартное поведение? (достойное объяснение того, почему очень полезно)
нет. По крайней мере, с typedef
s недоступен. Однако, обратите внимание, что:
class Nested {
// Not allowed since Nested has no access to PubDerived member data
void foo() {std::cout << pub << "\n";}
проблематично. Вложенный класс не имеет экземпляров PubDerived
для работы с ни pub
a static
объект-членов.
- можно ли ожидать, что он будет работать на самых современных компиляторы (т. е. как портативный это)?
да. Но проверьте документацию на соответствие стандартам. И всегда: попробуйте с несколькими компиляторами, такими как Comeau, в строгом режиме.
Я сделал все возможное, чтобы собрать все соответствующие положения из ISO/IEC 14882:1997.
9.7:класс, определенный внутри другого класса, называется вложенным. Имя вложенного класса локально для его класса включения. Вложенный класс находится в области включающего класса. За исключением использования явных указателей, ссылок и имен объектов, объявления во вложенном классе могут использовать только имена типа, статические члены и счетчиков из окружающего класса.
11.2.1 (должно быть вполне очевидно):
[...] Если класс объявлен базовым классом для другого класса с помощью спецификатора закрытого доступа, открытые и защищенные члены базового класса доступны как частные члены производного класса.
9.9 Имена Вложенных Типов:
имена типов подчиняются точно таким же правилам, как и другие имена.
затем в 11.8:
члены вложенного класса не имеют специального доступа ни к членам заключающего класса, ни к классам или функциям, которые предоставили дружбу заключающему классу; должны соблюдаться обычные правила доступа (11). Члены заключительного класса не имеют специального доступа к членам вложенного класса; должны соблюдаться обычные правила доступа (11).
однако Comeau C++, который, кажется, имеет лучшую стандартную поддержку, имеет то же поведение, что и GCC (позволяет fred
запрещает bar
С "error: type" Base:: priv_t" (объявлено в строке 4) недоступно").
Это не ответ на ваш вопрос, но по моему чтение в C++ чаво Лайт 24.6 то, что вы пытаетесь сделать, не допущено. Я не уверен, почему gcc разрешает это; вы пробовали это и в других компиляторах?