【问题标题】:Why can't friend function be recognized with using directive vs enclosing in namespace?为什么使用指令与封闭在命名空间中不能识别朋友功能?
【发布时间】:2020-03-20 13:00:37
【问题描述】:

为什么朋友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 中时,它会删除此错误,并且我会返回链接器错误。链接器错误不是这个问题是关于什么的。

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

【问题讨论】:

  • 解决方法很简单:只需将您的定义包含在namespace uiuc {} 中。恕我直言,这很常见。 ;-) 我只相信运算符定义放在全局命名空间中(忽略using namespace uiucfriend 声明)。因此,uiuc::Game 中的 friend 声明是不相关的。
  • 您不能使用using 在命名空间中定义函数。如果你有namespace uiuc {void foo();},那么你不能用using namespace uiuc; void foo() {...}定义它
  • 如果你想把它保存在头文件中,这意味着停止重复包含警告,只需将函数内联。
  • @heretoinfinity inline 关键字与函数是否内联没有直接关系。 inline 关键字的实际含义与函数定义是否可以(或必须)在多个翻译单元中重复有关。看我的回答。

标签: c++ header-files friend


【解决方案1】:

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

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

因此,您现在定义的函数是 global 命名空间范围内的 operator&lt;&lt;(std::ostream &amp; os, uiuc::Game &amp; game)(不是 uiuc::Gamefriend),而不是 @987654330 中的 operator&lt;&lt;(std::ostream &amp; os, Game &amp; 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;
  }
}

另外,关于链接器错误:如果您在类定义外部定义friend函数并且您没有将其指定为inline,那么它将是一个非-inline function,意思是在一个翻译单元中可能只有一个定义。这通常会阻止将定义放在标题中,因为标题通常包含在多个翻译单元中。

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

【讨论】:

    猜你喜欢
    • 2011-04-17
    • 1970-01-01
    • 2011-06-03
    • 2012-04-09
    • 1970-01-01
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多