Почему нельзя распознать дружественную функцию с помощью директивы против включения в пространство имен? - PullRequest
0 голосов
/ 20 марта 2020

Почему друг operator<< не может быть распознан, если он определен в файле cpp и директиве using? У меня в заголовочном файле game.h

namespace uiuc {

class Game {
public:
  Game();
  void solve();

  friend std::ostream & operator<<(std::ostream & os, Game & game);

private:
  std::vector<Stack> stacks;
};

И в моем cpp файле game.cpp:

#include "game.h"

using namespace  uiuc;
std::ostream & operator<<(std::ostream & os, Game & game) {
  for (unsigned long i = 0; i < game.stacks.size(); ++i) {
    os << "Stack [" << i << "]: " << game.stacks[i] << std::endl;
  }

  return os;
}

Я получаю ошибку:

g++ -std=c++1z -g -Wfatal-errors -Wall -Wextra -pedantic -MMD -MP  -c game.cpp
game.cpp:5:38: fatal error: 'stacks' is a private member of 'uiuc::Game'
  for (unsigned long i = 0; i < game.stacks.size(); ++i) {
                                     ^
./game.h:17:22: note: declared private here
  std::vector<Stack> stacks;
                     ^
1 error generated.
make: *** [Makefile:18: game.o] Error 1

Это сработало, когда функция Friend была определена в заголовочном файле. Я решил переместить его, так как получал ошибку компоновщика для дублированного символа для того же метода, поэтому я решил посмотреть, что произойдет, если я перенесу определение функции в файл cpp. Что мне не хватает? Однако, когда я заключаю определение в namespace uiuc, оно удаляет эту ошибку, и я снова возвращаюсь к ошибке компоновщика. Ошибка компоновщика НЕ о чем этот вопрос.

Почему компилятор не может понять, что это функция друга и поэтому может обращаться к закрытым переменным?

1 Ответ

1 голос
/ 20 марта 2020

Функции, впервые объявленные как friend в классе, помещаются в окружающее пространство имен, поэтому определяемая вами функция должна быть определена в пространстве имен uiuc.

Возможно, вы предполагаете, что если вы используете using namespace uiuc; новые объявления / определения будут помещены в пространство имен uiuc, но это не так. using namespace влияет только на поиск имен, но не там, где размещаются объявления / определения.

Поэтому функция, которую вы определяете в данный момент, - это operator<<(std::ostream & os, uiuc::Game & game) в области действия global пространства имен (которая не является friend из uiuc::Game), не operator<<(std::ostream & os, Game & game) в пространстве имен uiuc.

Поэтому вам необходимо правильно открыть пространство имен:

#include "game.h"

namespace uiuc {
  std::ostream & operator<<(std::ostream & os, Game & game) {
    for (unsigned long i = 0; i < game.stacks.size(); ++i) {
      os << "Stack [" << i << "]: " << game.stacks[i] << std::endl;
    }

    return os;
  }
}

Также, относительно ошибка компоновщика: если вы определяете friend функцию вне определения класса и не указываете его как inline, то это будет не встроенная функция , это означает, что для него может быть только одно определение только в одной единице перевода. Как правило, это не позволяет поместить определение в заголовок, поскольку заголовок обычно включается в несколько единиц перевода.

Если вы определяете функцию внутри тела класса или объявляете ее с помощью inline ключевым словом, тогда это будет встроенная функция , означающая, что она должна быть определена в каждой единице перевода, использующей ее, что обычно означает, что определение должно быть помещено в заголовочный файл.

...