Я недавно обнаружил, что область действия объявлений друзей соответствует чрезвычайно своеобразным правилам - если у вас есть объявление (определение) friend
для функции или класса, который еще не объявлен, он автоматически объявляется (определяется) ) в непосредственно включенном пространстве имен , но оно невидимо для неквалифицированного и квалифицированного поиска; однако объявления друзей function остаются видимыми в зависимости от аргументов.
struct M {
friend void foo();
friend void bar(M);
};
void baz() {
foo(); // error, unqualified lookup cannot find it
::foo(); // error, qualified lookup cannot find it
bar(M()); // ok, thanks to ADL magic
}
Если вы посмотрите на стандарт (см. связанный ответ ), они пошли на многое, чтобы включить это эксцентричное поведение, добавив конкретное исключение в квалифицированном / неквалифицированном поиске со сложными правилами. Конечный результат кажется мне чрезвычайно запутанным 1 , с еще одним угловым случаем, который нужно добавить в реализации. Как либо
- требующие
friend
объявлений для ссылки на существующие имена, точка; или
- позволяя им объявлять вещи такими, какие они есть сейчас, но без изменения поиска обычных имен (таким образом, такие имена становятся видимыми, как если бы они были объявлены "нормально" в окружающем пространстве имен)
Кажется, проще реализовать, уточнить и, самое главное, понять, мне интересно: почему они заморачивались с этим беспорядком? Какие варианты использования они пытались охватить? Что нарушает какое-либо из этих более простых правил (в частности, второе, которое наиболее похоже на существующее поведение)?
Например, в данном конкретном случае
struct M {
friend class N;
};
N *foo;
typedef int N;
вы получаете комично-шизофренические сообщения об ошибках
<source>:4:1: error: 'N' does not name a type
N *foo;
^
<source>:5:13: error: conflicting declaration 'typedef int N'
typedef int N;
^
<source>:2:17: note: previous declaration as 'class N'
friend class N;
^
, где компилятор сначала утверждает, что такого понятия, как N
, не существует, но сразу перестает играть глупости, когда вы пытаетесь предоставить конфликтующее объявление.