Создание неопределенного класса в качестве друга и определение его позже - PullRequest
7 голосов
/ 25 ноября 2010

Создание неизвестного друга

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 ]

6 голосов
/ 25 ноября 2010

Да, ваш код неверен ! Это интересная демонстрация того, как шаблоны могут изменять смысл кода тонкими способами. Следующий код является действительным:

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- инициализаторы в этом классе (включая такие вещи во вложенных классах).

Игнорируя несколько педантичных моментов, которые сделали бы эту формулировку неприменимой к этому (которые были дефектом, но исправлены в версии этого параграфа на 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 уже был объявлен, и этот поиск поразит его и выдаст ошибку, потому что это шаблон, а не не шаблонный класс.

3 голосов
/ 25 ноября 2010

// Запустите это - теперь он скомпилируется для вас

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;

}
2 голосов
/ 25 ноября 2010

Код неверно сформирован и 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>;  
};
...