Friend运算符重载会导致“已定义的”链接器错误

时间:2012-02-14 09:47:57

标签: c++ linker operator-overloading

我偶然发现了一个我可以解决的问题,但我不确定它为什么不起作用。

这是我尝试使用的代码。为了简洁起见,已经剥离了字段。让我知道他们是否需要,我会把它们放回去:

#pragma once
#ifndef __PageStyle__
#define __PageStyle__
class PageStyle
{
public:
    friend bool operator<(const PageStyle& lhs, const PageStyle& rhs);
};

bool operator<(const PageStyle& lhs, const PageStyle& rhs)
{
    return (lhs.name < rhs.name);
}
#endif

在我的源文件中,我做了类似的事情:

#include "PageStyle.h"

...
void PageStyleManager::loadPageStyles() {
    std::set<PageStyle> pageStyles;
    ...
}

代码编译得很好,但链接器吐了出来:

1>PageStyleManager.obj : error LNK2005: "bool __cdecl operator<(class PageStyle const &,class PageStyle const &)" (??M@YA_NABVPageStyle@@0@Z) already defined in BaseContentFiller.obj

BaseContentFiller是PageStyleManager的基类,也是以类似方式使用PageStyle的其他类。

经过深入研究后,我发现为了我的目的(使用STL集中的类)我毕竟不需要非成员朋友版本。 我让运营商成为内联公共成员,代码链接没有问题

为什么会出现这个问题?我确保我使用了标头防护装置,这是我第一次使用操作员重载的真实体验,我想知道我做错了什么。

2 个答案:

答案 0 :(得分:10)

如果您在头文件中包含该功能的定义,则会在包含头文件的每个 Translation unit 中定义该功能。
这违反了 One Definition Rule ,因此违反了链接器错误。

请注意,标头保护或#pragma once只是阻止相同的头文件多次包含在同一源文件中,但不会在不同的TU中定义它。

有两种解决方案可以解决问题:

  1. 只需在头文件和定义中添加声明,只需添加一个cpp文件或
  2. 即可
  3. 将标题中定义的函数设为inline

答案 1 :(得分:6)

不要在头文件中定义operator<;只需在那里声明它,并在.cpp文件中定义它。 #pragma once只是使头文件在同一.cpp文件中多次包含,但它可能包含在不同的.cpp文件中,在这种情况下,链接器将看到多个定义operator<