Рассмотрим класс внутри пространства имен. Определение класса объявляет функцию друга.
namespace Foo
{
class Bar
{
friend void baz();
};
}
Это должно, исходя из того, что я знаю, объявить baz()
как элемент самого внутреннего вмещающего пространства имен, т.е. Foo
,
Поэтому я ожидал следующее определение baz()
чтобы быть правильным:
void Foo::baz() { }
Однако GCC (4.7) дает мне ошибку.
error: ‘void Foo::baz()’ should have been declared inside ‘Foo’
Несколько решений, кажется, работают:
декларировать baz()
вне класса.
namespace Foo
{
void baz();
class Bar
{
friend void baz();
};
}
определять baz()
внутри пространства имен.
namespace Foo
{
class Bar
{
friend void baz();
};
}
...
namespace Foo
{
void baz() { }
}
Компилировать с -ffriend-injection
флаг, который устраняет ошибку.
Эти решения кажутся несовместимыми с общими правилами объявления / определения в C ++, которые я знаю.
Почему я должен объявить baz()
дважды?
Почему определение иначе только допустимо в пространстве имен и недопустимо с оператором разрешения контекста?
Почему флаг устраняет ошибку?
Почему я должен объявлять baz () дважды?
Потому что объявление друга не предоставляет полезного объявления функции в пространстве имен. Он объявляет, что, если эта функция объявлена в этом пространстве имен, она будет другом; и если бы вы определять дружественная функция внутри класса, тогда она будет доступна через зависимый от аргумента поиск (но не иначе), как если бы она была объявлена в пространстве имен.
Почему определение иначе только допустимо в пространстве имен и недопустимо с оператором разрешения контекста?
Потому что она не была (должным образом) объявлена в пространстве имен, и функция может быть определена вне ее пространства имен (с разрешением области действия), если она была объявлена.
Почему флаг устраняет ошибку?
Потому что флаг заставляет объявление друга действовать как объявление в пространстве имен. Это для совместимости с древними диалектами C ++ (и, по-видимому, некоторыми современными компиляторами), в которых это было стандартным поведением.
Первый кусок кода должен скомпилироваться:
namespace A {
struct B {
friend void foo();
};
}
void A::foo() {}
Хотя эта конкретная функция не может быть используемый если вы также не предоставите объявление на уровне пространства имен. Причина в том, что объявления друзей видны только через Argument Dependent Lookup (ADL), но foo
не зависит от A::B
и, следовательно, компилятор никогда не будет заглядывать внутрь этого типа.