Объявление / определение функции друга в пространстве имен

Рассмотрим класс внутри пространства имен. Определение класса объявляет функцию друга.

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() дважды?
Почему определение иначе только допустимо в пространстве имен и недопустимо с оператором разрешения контекста?
Почему флаг устраняет ошибку?

2

Решение

Почему я должен объявлять baz () дважды?

Потому что объявление друга не предоставляет полезного объявления функции в пространстве имен. Он объявляет, что, если эта функция объявлена ​​в этом пространстве имен, она будет другом; и если бы вы определять дружественная функция внутри класса, тогда она будет доступна через зависимый от аргумента поиск (но не иначе), как если бы она была объявлена ​​в пространстве имен.

Почему определение иначе только допустимо в пространстве имен и недопустимо с оператором разрешения контекста?

Потому что она не была (должным образом) объявлена ​​в пространстве имен, и функция может быть определена вне ее пространства имен (с разрешением области действия), если она была объявлена.

Почему флаг устраняет ошибку?

Потому что флаг заставляет объявление друга действовать как объявление в пространстве имен. Это для совместимости с древними диалектами C ++ (и, по-видимому, некоторыми современными компиляторами), в которых это было стандартным поведением.

4

Другие решения

Первый кусок кода должен скомпилироваться:

namespace A {
struct B {
friend void foo();
};
}
void A::foo() {}

Хотя эта конкретная функция не может быть используемый если вы также не предоставите объявление на уровне пространства имен. Причина в том, что объявления друзей видны только через Argument Dependent Lookup (ADL), но foo не зависит от A::Bи, следовательно, компилятор никогда не будет заглядывать внутрь этого типа.

1

По вопросам рекламы [email protected]