我正试图以标准方式重载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命名空间中。
答案 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;必须在命名空间中声明
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);
}
#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;)的类只要它们永远不会输出到流中,因为这个在这种情况下,函数永远不会被实例化。