2010-01-22 57 views
2

我一直在開發的主要是模板函數庫和管理,以保持組織(在某種程度上)東西,在下列方式:創建的模板函數庫

// MyLib.h 
class MyLib 
{ 
    template<class T> 
    static void Func1() 
    { 
    } 

    template<class T> 
    static void Func2() 
    { 
    } 
}; 

而且顯然要願意作出這個:

MyLib::Func1(); 

正如你所看到的,隨着更多功能的添加,這可能會變得非常難看。至少,我想將它分成不同的文件!

我最初認爲定義在MyLib命名空間在不同的文件功能的批次,然後用MyLib.h鞏固所有的人,但我一直得到了鏈接錯誤的卡車 - 當然,我可以把這個方法,如果仔細看這是建議。

有什麼想法?

PS:由於大多數這些函數有不同的目標,所以將它們歸類到我們實例化對象的類下是沒有意義的。我在這裏使用了一個class,所以我不必擔心我定義函數的順序(在MyLib之間也有相互依賴的函數)。

鏈接器錯誤:

所以基本結構是這樣的:我有兩個類(說& B)編譯爲靜態庫和運行這些類的實例的主應用。這些類A & B使用MyLib中的函數。當A & B正在編譯時,我得到LNK4006警告,其中說明屬於MyLib的符號已經在項目中的OBJ文件中定義,並且它忽略了它。

當它歸結到它成爲一個LNK2005錯誤,指出它是在一個& B.

更新OBJ文件中已經定義的應用程序: 謝謝Mike & Mathieu內聯的想法 - 這是問題!

除了一個問題:我有我明確專業化,這些都導致already defined錯誤(LNK2005)一些模板函數:

template<class t> int Cvt(){} 
template<> int Cvt<unsigned char>(){return 1;} 
template<> int Cvt<char>(){return 2;} 
template<> int Cvt<unsigned short>(){return 3;} 

任何想法?

Conlusion:

通過定義模板功能,在一個單獨的文件解決了明確的分工問題 - 感謝您的幫助!

+0

這是正確的做法。就個人而言,我不會將它們合併爲一個「包含全世界」標題,因爲這樣會增加編譯時間。這樣做不應該有鏈接器錯誤 - 你能舉一個例子或兩個? – 2010-01-22 13:56:27

+0

更新爲鏈接器錯誤 – Jacob 2010-01-22 15:53:21

+0

我們必須查看鏈接器錯誤和相關功能來診斷問題。你是否在庫中創建命名對象?一般來說,你不應該那樣做。 – 2010-01-22 16:26:07

回答

5

你應該更喜歡的命名空間在你的類靜態方法:

  • 命名空間爲您提供的可能性,在幾個文件共享,每個方法的邏輯組一個
  • 命名空間可以被省略:或者是因爲ADL踢或using myNamespace::MyFunc;(注:這是不好的做法寫using myNamespace;,你應該回避的做法)

現在,讓我們說話的組織:

  • 這是很好的做法,有你的文件層次陰影的命名空間層次結構[1]
  • 它通過邏輯組來分割你的方法,使用戶不必僅僅包括因爲他希望整個世界的良好做法Hello, World!要打印,商品標題可以幫助,但(即標頭做一堆包括懶惰的程序員使用)

[1]這裏是我的意思是:

#include "lib/string/manip.hpp" // Okay, this files come from "lib" 

int main(int argc, char* argv[]) 
{ 
    std::string s; 
    lib::string::manip(s);   // Same hierarchy, easy to remember the header 
    return 0; 
} 

一個激勵的例子?提升它(商品標題)。

更重要的是,這並沒有多少成本:只需將class替換爲namespace並刪除static關鍵字,這就是所有人。

對於連接問題:未模板化應該是聲明爲inline(儘量避免它,除非他們是單行)或(在一個單獨的文件.cpp)頭之外定義的所有方法。

UPDATE:

模板專業化的問題是,你最終定義現在的「正常」的方法:沒有什麼模板任何關於它的長,一旦你固定的每一個參數。因此,解決方案就像您對普通函數所做的那樣:在頭文件中聲明並在源文件中定義(因此只有一次)。

要更具體地瞭解這個奇怪的錯誤:C++的問題在於每個源文件都是單獨編譯的:預處理器將採用include並實際創建一個包含每個包含文件的文本文件(按順序),然​​後結束你的源代碼。編譯器接收這個文件並生成一個「.o」文件(用於gcc)。然後鏈接程序開始嘗試實際創建一個庫(或二進制文件)「。o「文件,並檢查每種方法只定義一次,否則它將如何選擇多個定義(不幸的是不檢查它們是否等同或不...)。模板方法和類,並且它在所有實例化中隨機選取一個(每個模板參數組合的一個實例化)。當然,這裏假設它們都是相同的,你可能會得到相當的頭痛類似:

// foo.h 
template <class T> int foo(T) { return 10; } 

// foo.cpp 
#include "foo.h" 

char a; 
std::cout << foo(a) << std::endl; 

// bar.cpp 
#include "foo.h" 

template <> int foo<char>(char) { return 20; } 

char b; 
std::cout << foo(b) << std::endl; 

這兩條線將打印相同的輸出,無論是10或20是未知的,雖然,可能會改變之間的建立!

+0

感謝您的詳細發佈以及內聯建議,但正如我在更新中所述,我在明確的專業化方面存在問題,有什麼建議? – Jacob 2010-01-22 19:42:36

4

MyLib命名空間是顯而易見的方式 - 畢竟,這基本上是如何標準庫,它可能比你的大得多。使用模板獲得大量鏈接器錯誤是非常不尋常的,除非你有很多前向聲明 - 你通常應該儘量避免。

+0

'std'肯定更大:) - 謝謝,我仔細看看命名空間的方式! – Jacob 2010-01-22 13:56:44

+0

我沒有任何前向聲明 - 我已經添加了對我已經獲得的鏈接器錯誤的描述。 – Jacob 2010-01-22 16:20:09

3

使用命名空間是正確的方法。就個人而言,我不會將它們合併爲一個「包含全世界」標題,因爲這樣會增加編譯時間。其他人可能更喜歡單個標題的便利性。

如果有任何非模板函數,那麼它們必須聲明爲inline,或者只在一個源文件中實現。模板函數和類定義中實現的類成員函數隱含地爲inline,但其他函數不是。

+0

謝謝你的**內聯**主意!我仍然有明確的專業化問題......任何想法? – Jacob 2010-01-22 19:41:54