2012-02-14 78 views
1

我偶然發現了一個問題,我可以解決,但我不知道爲什麼它不起作用。朋友操作員重載導致「已定義在」鏈接器錯誤

這是我嘗試使用的代碼。爲了簡潔起見,字段已被刪除。讓我知道你在需要他們,我會把它們放回:

#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 &)" ([email protected][email protected]@[email protected]) already defined in BaseContentFiller.obj 

BaseContentFiller爲PageStyleManager基類,以及用於其它類以類似的方式也使用PageStyle。

經過深入研究,我發現爲了我的目的(使用STL集合中的類),我真的不需要非成員的朋友版本。 我讓操作員成爲在線公衆會員,並且代碼鏈接沒有問題

爲什麼會出現這個問題?我確保我使用了標頭守衛,這是我第一次真正體驗操作符重載,並且我想知道我做錯了什麼。

+1

除非它們是靜態的,否則不應將非成員函數的*實現*放在頭文件中。 – 2012-02-14 09:55:50

+2

兩個無關的問題:'#pragma once'沒有被普遍理解,並且在包含守衛之前擁有它可能會阻止某些替代技術的運行。如果你確實使用了'#pragma once',那就把它放在include guard後面。並且符號中的雙下劃線是未定義的行爲,即使符號是包含警衛。 – 2012-02-14 10:42:12

回答

9

如果在頭文件中包含該函數的定義,則會在包含頭文件的每個Translation unit中定義它。
這違反了One Definition Rule並因此鏈接器錯誤。

請注意,標頭防護或#pragma once只是防止同一個頭文件多次包含在同一個源文件中,但不會在不同的TU中定義它。

兩種解決方案來解決這個問題:

  1. 只需添加在頭文件中的聲明和定義只有一個cpp文件或
  2. 做在標題定義爲inline功能。
+0

將聲明移動到cpp文件工作,謝謝!我從字面上理解了各種在線教程(他們經常在頁面上顯示標題和源代碼)。回想起來,在源文件中聲明運算符是非常合理的。 – seanhodges 2012-02-14 12:16:07

6

不要在頭文件中定義operator<;只要在那裏聲明它,並在.cpp文件中定義它。 #pragma once僅使頭文件在同一個.cpp文件中多次包含,但它可能包含在不同的.cpp文件中,在這種情況下,鏈接器將看到operator<的多個定義。

+0

這個答案也很有用,謝謝Aasmund。我選擇了另一個,只是因爲鏈接到額外的資源。 – seanhodges 2012-02-14 12:17:20

+0

而不是標記你的答案解決,我已upvoted,所以你仍然得到你的10K代表:) – seanhodges 2012-02-14 12:22:12

+0

@seanhodges:不知何故,我覺得非常滿意...:D – 2012-02-14 12:25:58