Подтвердить что ты не робот

Как я могу объявить функцию друга в пространстве имен, который принимает внутренний класс в качестве параметра?

Рассмотрим этот код:

namespace foo {}

class A
{
   class B
   {
   };

   friend int foo::bar( B& );
};

namespace foo
{
   int bar( A::B& )
   {
   }
}

g++ 4.4.3 говорит мне:

friendfun-innerclass.cpp: 21: ошибка: 'int foo:: bar (A:: B &)' должен иметь был объявлен внутри 'foo'

Но я не могу объявить:

namespace foo
{
   int bar( A::B& );
}

перед определением класса A, поскольку A:: B не был объявлен. И я не могу объявить "класс A:: B", очевидно, объявить класс BI должен дать определение класса A, и насколько я знаю, объявления "friend" должны быть внутри определения класса A.

Что странно для меня, если я беру функцию "bar()" из пространства имен foo, все работает нормально. Мне кажется нелогичным то, что наличие функции внутри пространства имен или внутри пространства имен изменит, будет ли компилятор принимать декларацию функции друга в классе.

Знает ли кто-нибудь о том, как правильно структурировать все объявления и т.д., чтобы заставить это работать?

4b9b3361

Ответ 1

Невозможно выполнить то, что вы хотите, потому что вам придется переслать объявление вложенного класса (который вы не можете), чтобы предоставить прототип для foo::bar.

Как первая попытка обойти эту проблему, я бы, вероятно, прибегнул к созданию шаблона функции foo::bar. Таким образом, компилятор будет разрешать типы после A и B.

Жгут проводов:

namespace foo
{
    template<class B> int bar(B&);
};

class A
{
   class B
   {
       template<class B> friend int foo::bar( B& );
       int n_;
   public:
       B() : n_(42) {}
   };

public:
    B b_;
};

template<class B> int foo::bar(B& b)
{
    return b.n_;
}

int main()
{
    A a;
    foo::bar(a.b_);
}

Ответ 2

Я считаю, что вам нужно ::foo::bar.

Ответ 3

Вы не можете, потому что вы не можете переадресовать внутренние классы.

Это близко, как вы собираетесь.

namespace foo {
    class A_B;
    int bar(A_B &);
}

struct A
{
   class B
   {
   };

   friend int foo :: bar (A_B&);
};

namespace foo
{
   struct A_B : public A :: B {
     // constructors, delegated if you're C++11 :-)
   };

   int bar (A_B &)
   {
   }
}

Вы теряете частную собственность A::B, но если вы думаете об этом, это имеет смысл или вы не сможете реализовать foo::bar.

Ответ 4

Не похоже, что вы можете это сделать. То, что вы можете сделать, - это объявить класс со статическим членом, а класс получит дружбу. Но в любое время, когда вы используете дружбу, по крайней мере, еще раз взгляните на ваш дизайн и спросите себя, почему. Это может быть хорошо или может быть лучший ответ.

namespace foo { class bar; }

class A
{
   class B
   {
   };

   friend class ::foo::bar;
};

namespace foo
{
   class bar
   {
   public:
      static int run_bar( A::B& )
      {
      }
   };
}

Ответ 5

Вы не можете этого сделать, потому что нет возможности написать объявление вперед A::B и, следовательно, переслать объявление foo(A::B&).

Однако вы можете использовать трюк, известный как "вставка имени друга", чтобы объявить (и, возможно, определить) функцию друга в определении класса. Таким образом, вы можете вызвать эту функцию друга без квалификаторов пространства имен (например, bar(b) вместо foo::bar(b)), а зависящий от аргумента поиск будет успешно разрешать вызов:

struct A
{
    struct B {};

    friend int bar(B&) { // inject bar() into the outer namespace
        // implementation here
    }
};

namespace foo
{
   int foo()
   {
       A::B b;
       return bar(b); // it calls bar(B&) declared in A
   }
}