Почему нельзя переслать объявленный класс друга в класс?

Следующий код не компилируется:

struct X {
  friend class Y;
  Y* ptr;
};

В cppreference описывается ситуация как

... Если имя класса, которое используется в объявлении друга, еще не объявлено, оно объявляется прямо на месте.

Если "пятно" означает, где объявляется связь друга, тогда должно быть хорошо объявить член Y* ptr. Почему он не компилируется? Где в стандарте запрещается это?

Ответ 1

Это ошибка на сайте. Это противоречит стандарту, в котором говорится, что декларация о дружбе не заменяет форвардную декларацию:

7.3.1.2.3 Каждое имя, впервые объявленное в пространстве имен, является членом этого пространства имен. Если объявление друга в нелокальном классе сначала объявляет класс, функцию, шаблон шаблона или шаблон функции, он является членом самого внутреннего охватывающего пространства имен. Объявление друга само по себе не делает имя видимым для неквалифицированного поиска или квалифицированного поиска.

Часть о том, что имя не отображается для неквалифицированного или квалифицированного поиска, по существу означает, что имя не ведет себя как декларация вперед.

Ответ 2

В дополнение к ответу @dasblinkenlight, примечание в 3.3.2. Точка декларации горит . 10 явно говорит, что объявление друга не вводит (и, следовательно, не "forward declare") имя класса:

10 [Примечание: объявления Friend относятся к функциям или классам, которые являются членами ближайшего охватывающего пространства имен, но не вводят новые имена в это пространство имен ([namespace.memdef]).

Ответ 3

Один из способов исправить этот код - просто добавить ключевое слово class:

struct X {
    friend class Y;
    class Y* ptr;
};

Это приведет к объявлению class Y в глобальной области, если X находится в глобальной области.