为什么不能通过 using 指令 ​​vs 封闭在命名空间中来识别友元函数?

Why can't friend function be recognized with using directive vs enclosing in namespace?

为什么朋友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

这在头文件中定义友元函数时有效。我决定移动它,因为对于相同的方法,我收到一个重复符号的链接器错误,所以我决定看看如果我将函数定义移动到 cpp 文件会发生什么。我错过了什么?然而,当我将定义包含在 namespace uiuc 中时,它消除了这个错误,我又回到了链接器错误。链接器错误 NOT 这个问题是关于什么的。

为什么编译器不能意识到这是一个友元函数并且可以访问私有变量?

首先在 class 中声明为 friend 的函数放置在封闭的命名空间中,因此您要定义的函数必须在 uiuc 命名空间中定义。

您可能假设如果您使用 using namespace uiuc;,新的 declarations/definitions 将被放入 uiuc 命名空间,但事实并非如此。 using namespace 只影响名称的查找,而不影响放置 declarations/definitions 的位置。

因此,您现在定义的函数是 global 命名空间范围内的 operator<<(std::ostream & os, uiuc::Game & game)(它不是 frienduiuc::Game),而不是 uiuc 命名空间中的 operator<<(std::ostream & os, Game & game)

所以你需要正确打开命名空间:

#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;
  }
}

此外,关于链接器错误:如果您在 class 定义之外 定义了 friend 函数,并且没有将其指定为 inline,那么它将是一个non-inline函数,意味着它可能只有一个定义在只有一个 翻译单元。这通常会阻止将定义放在 header 中,因为 header 通常包含在多个翻译单元中。

如果您在 class body 中 内部定义函数 body 或使用 inline 关键字声明它,那么它将是一个 内联函数,意思是必须在每个使用它的翻译单元中定义它,这通常意味着定义必须放在header文件中。