Создание неопределенного класса в качестве друга и определение его позже
Создание неизвестного друга
template<typename T>
class List
{
protected:
class a {
int x;
int y;
private:
friend class b; // <------------ Why this is not an error?
};
template <typename U > class b { //If that is not a error this should be an error
int z;
U y;
};
public:
List() {
a* ptr = (a *)new unsigned char[sizeof(a)];
}
};
int main() {
List<int> mylist;
}
Пожалуйста, перейдите по этой ссылке, у меня есть мои вопросы в виде комментариев в коде. Я пытаюсь сделать другого класса другом моего класса. Но этот класс не знаю во время создания друга. Что такое правило C++, которое допускает это? Позже я определю этот класс таким образом, чтобы он был несовместим с объявлением друга. Почему это не выдает ошибку. Спасибо
3 ответа
Да, ваш код неверен! Это интересная демонстрация того, как шаблоны могут незаметно изменять смысл кода. Следующий код действителен:
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
class b {
List::a::type an_int; // allowed to access private member
};
Стандарт говорит в 7.3.1.2/3
Если объявление друга в нелокальном классе сначала объявляет класс или функцию83), класс или функция друга является членом самого внутреннего окружающего пространства имен.
Когда это "первый объявленный класс"? Там тоже сказано
При поиске предварительного объявления класса или функции, объявленной в качестве друга, и когда имя класса или функции друга не является ни квалифицированным именем, ни идентификатором шаблона, области действия за пределами самой внутренней охватывающей области пространства имен не рассматриваются.
Поиск для "класса b" делегирован от 7.1.5.3/2 до 3.4.4, который, в свою очередь, делегирует поиску неквалифицированного имени в 3.4/7. Теперь весь вопрос в том, видимо ли имя шаблона "b" в классе объявлений друзей a. Если это не так, имя не найдено, и объявление друга будет ссылаться на новый объявленный класс в глобальной области видимости. 3.3.6/1 о масштабах это говорит
Потенциальная область действия имени, объявленного в классе, состоит не только из декларативной области, следующей за декларатором имени, но также из всех тел функций, аргументов по умолчанию и конструктора ctor- initializer в этом классе (включая такие вещи во вложенных классах).
Игнорируя несколько педантичных моментов, которые сделали бы эту формулировку неприменимой к этому (которые были дефектом, но исправлены в версии этого параграфа на C++0x, что также облегчает чтение), этот список не включает объявление друга как область, где это имя шаблона видно.
Тем не менее, друг был объявлен в классе члена шаблона класса. Когда создается экземпляр класса-члена, применяется другой поиск - поиск имен друзей, объявленных в шаблоне класса! Стандарт говорит
Дружественные классы или функции могут быть объявлены в шаблоне класса. Когда создается экземпляр шаблона, имена его друзей обрабатываются так, как если бы специализация была явно объявлена в момент его создания.
Поэтому следующий код недействителен:
template<typename T>
class List
{
public:
class a {
typedef int type;
friend class b; // that's fine!
};
template <typename U > class b;
};
// POI
List<int>::a x;
Когда это вызывает List<int>::a
чтобы быть неявно созданным, имя a
выглядит как "// POI", как если бы была объявлена явная специализация. В этом случае шаблон List::b
уже объявлен, и этот поиск поразит его и выдаст ошибку, потому что это шаблон, а не не шаблонный класс.
// Запустим это - теперь оно скомпилируется для вас
template <typename U > class b; //<----- forward declaration
template<typename T>
class List
{
protected:
class a {
int x;
int y;
private:
friend class b<T>; // <------------ Add <T>
};
template <typename U > class b {
int z;
U y;
};
public:
List() {
a* ptr = (a *)new unsigned char[sizeof(a)];
}
};
int main() {
List<int> mylist;
}
Код плохо сформирован, и Comeau отклоняет его, выдавая следующую ошибку
error: invalid redeclaration of type name "b" (declared at
line 11)
Я думаю, что это ошибка в g++. Intel C++ также отвергает это. Вы можете исправить код, определив класс B
выше A
,
template <typename U > class b {
int z;
U y;
};
class a {
int x;
int y;
private:
friend class b<T>;
};