覆盖运算符时的友谊问题<<

时间:2011-11-06 02:19:01

标签: c++ linker operator-overloading friend access-control

我正试图以标准方式重载operator<<。我有一个名为SymbolTable的类,它位于一个名为SymbolTable.h的文件中,如下所示:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table);
private:
  vector<map<int, Symbol*> > mSymbols;
};

// ...

}

operator<<的实现访问了SymbolTable的一些私有成员。这是实现的签名,以证明它与前向声明的签名没有区别(我复制粘贴它以确保我不会发疯。)

ostream& operator<<(ostream &out, const SymbolTable &table)
{ ... }

如果我将实现放在第二个...的SymbolTable.h中,我会收到以下链接器错误:

  

ld:/var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccstrYnU.o和/ var中的重复符号Compaler :: operator&lt;&lt;(std :: basic_ostream&gt;&amp;,Compaler :: SymbolTable const&amp;) /folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o for architecture x86_64

但是,如果我把它放在SymbolTable.cpp中,其余的我的SymbolTable成员实现,代码甚至不会编译;我收到与访问私人成员相关的错误,表明友谊未被识别:

  

../ SymbolTable / SymbolTable.h:在函数'std :: ostream&amp; operator&lt;&lt;(std :: ostream&amp;,const Compaler :: SymbolTable&amp;)':   ../SymbolTable/SymbolTable.h:75:错误:'std :: vector,std :: allocator&gt;,Compaler :: Symbol *,std :: less,std :: allocator&gt; &gt;,std :: allocator,std :: allocator&gt;,Compaler :: Symbol *&gt; &GT; &gt;,std :: allocator,std :: allocator&gt;,Compaler :: Symbol *,std :: less,std :: allocator&gt; &gt;,std :: allocator,std :: allocator&gt;,Compaler :: Symbol *&gt; &GT; &GT; &GT; &GT; Compaler :: SymbolTable :: mSymbols'是私有的   ../SymbolTable/SymbolTable.cpp:98:错误:在此上下文中

我做错了什么?

答案摘要:

感谢Seth Carnegie和Daniel R. Hicks,我已经弄明白了。为了将来可能犯同样错误的人的利益,我将在我的问题中总结答案,因为评论中有很多来回。

第一个(实现在SymbolTable.h中的重复符号)问题发生,因为我试图同时编译和链接包含SymbolTable.h的两个文件。我一直在误解#ifdef如何包括警卫的工作;也就是说,它们只会阻止代码在一个文件中被包含两次,但如果你试图链接两个包含第三个代码的文件,它们将无法帮助你。

第二个问题是,当我在SymbolTable.cpp文件中使用它时,我忘记将我的operator<<实现放在Compaler命名空间中。

3 个答案:

答案 0 :(得分:3)

如果您在头文件中执行此操作,则需要使用内联:

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };

    inline ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

如果你想在源文件中做。然后操作员&lt;&lt;必须在命名空间中声明

Header.h

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };
    ostream& operator<<(ostream &out, const SymbolTable &table);
}

Source.cpp

#include "Header.h"
namespace Compaler   // must be here
{    
    ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

答案 1 :(得分:2)

第一个问题是因为您将定义放在头文件中并#include两次。这会导致多重定义错误。

至于第二个问题,可能你的函数的签名与friend声明不同。

只是为了让您知道,作为获得正确签名的替代方法,您可以内联编写函数:

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table) {
      ...
  }
private:
  vector<map<int, Symbol*> > mSymbols;
};

编辑:由于显然签名是正确的,因此错误位于其他地方。你必须发布更多代码。

答案 2 :(得分:2)

正如其他答案和评论所指出的那样,可以将其作为朋友功能。但是,我经常喜欢的另一种方法是创建一个公共打印函数,它将对象打印到std :: ostream,然后让operator&lt;&lt;叫那个功能。这使您可以通过使运算符&lt;&lt;&lt;&lt;朋友的功能:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  void print(ostream & out); // basically your definition of operator<<, in your .cpp file
private:
  vector<map<int, Symbol*> > mSymbols;
};

// this doesn't have to be inline, but since it's a two-liner, there's probably no harm either
inline ostream& operator<<(ostream &out, const SymbolTable &table)
{
    table.print(out);
    return out;
}

// ...

}

这也为您的客户提供了避免&lt;&lt;&lt;&lt;&lt;语法,可能是也可能不是好事。

另一个技巧:如果你为多个类做这个,你可以创建运算符&lt;&lt;一个模板,以便任何实现print(ostream&amp;)的类型都可以输出到流:

template <typename T>
inline ostream& operator<<(ostream &out, const T &obj)
{
    obj.print(out);
    return out;
}

由于这是一个模板函数,因此对类的任何特定定义都将覆盖此定义,并且未定义print(ostream&amp;)的类只要它们永远不会输出到流中,因为这个在这种情况下,函数永远不会被实例化。