2010-05-13 50 views
29

我正在將我的C++應用程序的一部分從使用舊的C類型數組更改爲模板化的C++容器類。詳情請參閱this question。雖然解決方案運行良好,但是我對模板化代碼所做的每個小改動都會導致大量的重新編譯,從而大大減慢了構建時間。有沒有辦法將模板代碼從頭文件中取出並放回到cpp文件中,以便稍做實施更改不會導致重大重建?如何使用C++模板減少編譯時間

回答

14

我認爲一般規則適用。嘗試減少代碼部分之間的耦合。將過大的模板標題分解爲一起使用的較小的函數組,因此整個事件不必包含在每個源文件中。另外,儘量讓頭部快速進入穩定狀態,也許可以對較小的測試程序進行測試,所以當它們集成到一個更大的程序中時,它們不需要改變(太多)。

(與任何優化一樣,它可能不太值得模板打交道時,而不是尋找一個「算法」的優化,大大減少了擺在首位的工作負載來優化編譯器的速度。)

+3

+100您不會在一個巨大的無關項目中測試模板。模板在進入無關項目之前應儘可能沒有錯誤。 – jmucchiello 2010-05-13 15:02:58

+0

+1。單元測試也像瘋了! – 2010-05-13 15:35:03

+0

Oups,在我發佈我的之前沒有看到你回答,但我想我更清楚一點......朝着10k馬克的路線;)? – 2010-05-13 15:38:40

4
  • 你可以得到一個支持export關鍵字的編譯器,但這不太可能持續。

  • 您可以使用explicit instantiation,但不幸的是,這需要您預先提前使用的模板類型。

  • 如果您可以從算法中提取模板化類型,則可以將其置於其自己的.cc文件中。

  • 我不會建議這樣做,除非它是一個主要問題,但是:您可能能夠提供一個模板容器接口,通過調用void*實現來實現,您可以隨意更改該實現。

+3

'export'將被丟棄在C++ 0x中。你甚至不應該考慮現在使用它。 – pmr 2010-05-13 14:26:12

+0

+1爲明確實例,我以前沒有遇到過,並認爲它可以幫助很多。 – 2010-05-14 05:57:56

2

您可以在沒有模板的情況下定義基類,並將大部分實現移動到那裏。模板化數組然後將只定義代理方法,它使用基類來處理所有事情。

18

幾種方法:

  • export keyword理論上可以幫助,但它受到了不好的支持,在C++ 11被正式刪除。
  • 如果您可以提前預測您需要哪些實例化(如果您不介意維護此列表),則顯式模板實例化(請參閱herehere)是最直接的方法。
  • Extern templates,已經被幾個編譯器支持作爲擴展。我的理解是,extern模板不一定讓您將模板定義移出頭文件,但它們確實可以更快地編譯和鏈接(通過減少模板代碼必須實例化和鏈接的次數)。
  • 根據您的模板設計,您可能能夠將其大部分複雜性移動到.cpp文件中。標準示例是一個類型安全的向量模板類,它僅包裝類型不安全的向量void*;所有的複雜性都在駐留在.cpp文件中的void*向量中。 Scott Meyers在第二版中提供了 Effective C++(項目42,「明智地使用私有繼承」)中的更詳細示例。
+2

項目42:寧願遏制繼承你可以隨時...... – 2010-05-13 15:17:41

+1

我沒有最新版本。 – 2010-05-13 15:21:28

+0

「在我的副本(第3版)中,」明智地使用私有繼承「是39,但感謝指針。我真的應該重新閱讀邁爾斯的兩本有效的書。 – 2010-05-13 15:28:51

5

首先,爲了完整起見,我將介紹直接的解決方案:只在必要時使用模板化代碼,並將其基於非模板代碼(在其自己的源文件中實現)。

但是,我懷疑真正的問題是您使用泛型編程,因爲您會使用典型的OO編程,最終會出現一個臃腫的類。

讓我們舉個例子:

// "bigArray/bigArray.hpp" 

template <class T, class Allocator> 
class BigArray 
{ 
public: 
    size_t size() const; 

    T& operator[](size_t index); 
    T const& operator[](size_t index) const; 

    T& at(size_t index); 
    T const& at(size_t index); 

private: 
    // impl 
}; 

這是否衝擊嗎?可能不會。畢竟,它看起來很簡約。事情是,事實並非如此。

// "bigArray/at.hpp" 

template <class Container> 
typename Container::reference_type at(Container& container, 
             typename Container::size_type index) 
{ 
    if (index >= container.size()) throw std::out_of_range(); 
    return container[index]; 
} 

template <class Container> 
typename Container::const_reference_type at(Container const& container, 
              typename Container::size_type index) 
{ 
    if (index >= container.size()) throw std::out_of_range(); 
    return container[index]; 
} 

好吧,這細微的變化調用:

// From 
myArray.at(i).method(); 

// To 
at(myArray,i).method(); 

然而,由於柯尼希的查找,你可以給他們打電話不合格只要at方法可以在沒有任何一般性損失分解出來你把它們放在同一個命名空間中,所以這只是一個習慣問題。

這個例子是人爲設計的,但一般的觀點是站得住腳的。請注意,由於其通用性,at.hpp從來不必包含bigArray.hpp,並且仍然會產生嚴格的代碼,就好像它是成員方法一樣,只是我們可以根據需要在其他容器上調用它。

而現在,中BigArray用戶並不需要包括at.hpp,如果她不使用它......從而降低她的依賴,如果你在該文件中更改代碼不會受到影響:例如改變std::out_of_range調用功能文件名和行號,容器的地址,大小以及我們嘗試訪問的索引。

其他(不那麼明顯)的優點是,如果BigArray曾經完整性約束被違反,那麼at顯然是出原因,因爲它不能亂用類的內部,從而減少犯罪嫌疑人的數量。

這是由許多作者,如香草Sutters在C++ Coding Standards推薦:

項目44:寧願寫非成員nonfriend功能

,並在升壓被廣泛使用...但是你必須改變你的編碼習慣!

那麼當然你只需要包括你所依賴的東西,那裏應該是靜態C++代碼分析器報告包含但未使用的頭文件可以幫助解決這個問題。

+0

感謝您的迴應,並且您對模板代碼的臃腫非常正確。我最初基於它的MFC CArray,我發現它後來變得肥胖!哪個靜態代碼分析器列出未使用的標題?我目前使用PC-LINT並沒有看到該功能。 – 2010-05-14 05:56:11

+0

我認爲這將是一件普通的事情,但我擔心我會將它與另一種語言混淆:/這很奇怪,因爲即使Eclipse中的C++着色器檢查重載(並且只有在發現正確的重載時才着色)。 ..抱歉誤導你。 – 2010-05-14 11:48:50

+0

我知道這個訣竅,也是Scott Meyers推薦的。不過,我認爲這會對代碼的可讀性產生負面影響。像Visual Studio這樣的可視化程序也不會選擇全局函數,而且這些可能會加快您的開發速度。 Boost圖幾乎完全按照這種方式設計(即在抽象圖概念上運行免費函數),雖然它非常靈活且功能強大,但初學者很難避開它。 – gast128 2014-03-10 16:55:59

相關問題