我正在尝试以标准方式重载 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 中的重复符号 Compaler::operator<<(std::basic_ostream >&, Compaler::SymbolTable const&) 和/var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o 用于架构 x86_64
但是,如果我将其放入 SymbolTable.cpp
(我的其余 SymbolTable 成员实现所在的位置)中,则代码将获胜甚至不编译;我收到与访问私有成员相关的错误,表明友谊未被识别:
../SymbolTable/SymbolTable.h:在函数 'std::ostream& 中运算符 <<(std::ostream&, const Compaler::SymbolTable&)':
../SymbolTable/SymbolTable.h:75:错误:'std::向量,std::分配器>,Compaler::Symbol*,std::less,std::分配器> >、std::分配器、std::分配器 >、Compaler::Symbol*> > >、std::分配器、std::分配器 >、Compaler::Symbol*、std::less、std::分配器 > >、std::分配器、std::分配器 >、Compaler::Symbol*> > > > > Compaler::SymbolTable::mSymbols' 是私有的
../SymbolTable/SymbolTable.cpp:98: 错误:在此上下文中
我做错了什么?
答案摘要:
感谢 Seth Carnegie 和 Daniel R. Hicks,我已经弄清楚了。为了将来可能犯同样错误的人的利益,我将总结我的问题中的答案,因为评论中有很多来回。
第一个问题(当实现在 SymbolTable.h 中时出现重复符号)问题是因为我试图同时编译和链接包含 SymbolTable.h 的两个文件。我一直对 #ifdef
include Guards 的工作方式存在误解;也就是说,它们只会阻止代码在一个文件中包含两次,但如果您尝试链接两个文件,而这两个文件都包含第三个文件中的代码,则它们不会对您有帮助。
第二个问题是,当我将 operator<<
实现放在 SymbolTable.cpp 文件中时,我忘记将它放在 Compaler 命名空间中。
I'm trying to overload operator<<
in the standard way. I have a class called SymbolTable residing in a file called SymbolTable.h as follows:
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;
};
// ...
}
The implementation of operator<<
accesses some private members of SymbolTable. Here is the signature of the implementation, to demonstrate that it does not differ from the signature of the forward declaration (I copy-pasted it to be sure I wasn't going crazy.)
ostream& operator<<(ostream &out, const SymbolTable &table)
{ ... }
If I put the implementation in SymbolTable.h where the second ... is, I get the following linker error:
ld: duplicate symbol Compaler::operator<<(std::basic_ostream >&, Compaler::SymbolTable const&)in /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccstrYnU.o and /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o for architecture x86_64
However, if I instead put it in SymbolTable.cpp
, where the rest of my SymbolTable member implementations are, the code won't even compile; I get errors related to accessing private members, indicating that the friendship is not being recognized:
../SymbolTable/SymbolTable.h: In function ‘std::ostream& operator<<(std::ostream&, const Compaler::SymbolTable&)’:
../SymbolTable/SymbolTable.h:75: error: ‘std::vector, std::allocator >, Compaler::Symbol*, std::less, std::allocator > >, std::allocator, std::allocator >, Compaler::Symbol*> > >, std::allocator, std::allocator >, Compaler::Symbol*, std::less, std::allocator > >, std::allocator, std::allocator >, Compaler::Symbol*> > > > > Compaler::SymbolTable::mSymbols’ is private
../SymbolTable/SymbolTable.cpp:98: error: within this context
What am I doing wrong?
Summary of answers:
Thanks to Seth Carnegie and Daniel R. Hicks, I've got it figured out. For the benefit of anyone who might make the same mistakes in the future, I'm going to summarize the answers in my question because there was a lot of back and forth in the comments.
The first (duplicate symbol when the implementation was in SymbolTable.h) problem occurred because I was trying to simultaneously compile and link two files which included SymbolTable.h . I have been labouring under a misunderstanding of how #ifdef
include guards work; that is, they will only prevent code from being included twice in one file, but will not help you if you try to link two files which both included code from a third.
The second problem was that I forgot to put my operator<<
implementation in the Compaler namespace when I had it in the SymbolTable.cpp file.
发布评论
评论(3)
如果您在头文件中执行此操作,则需要使用 inline:
如果您想在源文件中执行此操作。然后运算符<< 中声明
必须在命名空间Header.h
Source.cpp
If you do it in the header file you need to use inline:
If you want to do in the source file. Then the operator<< must be declared in the namespace
Header.h
Source.cpp
第一个问题是因为您将定义放入头文件中并
#include
两次。这会导致多重定义错误。至于第二个问题,您的函数可能具有与
friend
声明不同的签名。只是为了让您知道,作为获得正确签名的替代方法,您可以内联编写函数:
编辑:由于显然签名是正确的,因此错误位于其他地方。您必须发布更多代码。
The first problem is because you're putting the definition in the header file and
#include
ing it twice. This causes a multiple definition error.As for the second problem, probably your function has a different signature than the
friend
declaration.Just to let you know, as an alternative to getting the signature right, you can write the function inline:
Edit: Since apparently the signature is right, the error lies somewhere else. You'll have to post more code.
正如其他答案和评论所指出的那样,可以将其作为好友功能来执行。然而,我经常喜欢的另一种选择是创建一个公共打印函数,它将对象打印到 std::ostream,然后让operator<<调用该函数。这可以让您避免通过使操作符<<来破坏封装。好友功能:
这也使您的客户可以选择避免 <<语法,这可能是好事,也可能不是好事。
另一个技巧:如果你对多个类执行此操作,你可以使operator<<模板,以便任何实现 print(ostream&) 的类型都可以输出到流:
由于这是一个模板函数,因此类的任何特定定义都将覆盖此定义,而未定义 print(ostream&) 的类;) 只要它们从不输出到流中就可以,因为在这种情况下该函数永远不会被实例化。
As other answers and comments have pointed out, it is possible to do this as a friend function. However, an alternative I often prefer is to make a public print function, which prints the object to an std::ostream, and then let operator<< call that function. This lets you avoid breaking encapsulation by making operator<< a friend function:
This also gives your clients the option to avoid the << syntax, which may or may not be a good thing.
Another trick: if you do this for multiple classes, you could make operator<< a template, so that any type implementing print(ostream&) can be output to a stream:
Since this is a template function, any specific definitions of it for a class will override this definition, and classes that don't define print(ostream&) are fne as long as they are never output to a stream, since this function will never be instantiated in that case.