[C++] C ++模板只是變相的宏?


Answers

它們由編譯器解析,而不是由在編譯器之前運行的預處理器解析。

以下是MSDN關於它的說明: http : //msdn.microsoft.com/zh-cn/library/aa903548(VS.71).aspx

這是宏的一些問題:

  • 編譯器無法驗證宏參數是否兼容。
  • 宏沒有任何特殊的類型檢查擴展。
  • 我和j參數被評估兩次。 例如,如果任一參數具有postincremented變量,則會執行兩次增量。
  • 由於宏由預處理器擴展,編譯器錯誤消息將引用擴展宏,而不是宏定義本身。 此外,宏在調試過程中將以擴展形式顯示。

如果這還不夠,我不知道是什麼。

Question

我已經用C ++進行了幾年的編程,而且我已經使用了STL,並且創建了自己的模板類幾次,看看它是如何完成的。

現在我正試圖將模板更深入地融入到我的面向對象的設計中,一個嘮叨的想法一直回到我身上:它們只是一個宏,真的...您可以使用#define實現(而不是UGLY)auto_ptrs,如果你真的想要。

這種關於模板的思考方式可以幫助我理解代碼的實際工作方式,但是我覺得我必須以某種方式忽略這一點。 宏指的是邪惡的化身,但“模板元編程”卻風靡一時。

那麼,真正的區別是什麼? 以及模板如何避免#define引導你進入的危險

  • 在你不指望他們的地方出現難以察覺的編譯器錯誤?
  • 代碼膨脹?
  • 跟踪代碼有困難嗎?
  • 設置調試器斷點?



模板理解數據類型。 宏不。

這意味著你可以做如下的東西...

  • 定義可以接受任何數據類型的操作(例如,用於包裝數字的操作 ),然後根據數據類型是整數還是浮點數,提供專門的選擇適當的算法
  • 在編譯時確定你的數據類型的各個方面,允許微軟使用它的C ++重載strcpy_s及其流派的技巧,如數組大小的模板演繹

另外,因為模板是類型安全的,所以有一些模板編碼技術可以想像用一些假設的高級預處理器來執行,但是最好是模糊和易出錯的(例如, 模板模板參數 ,默認模板參數,策略模板為在現代C ++設計中討論)。




呈現typename關鍵字以啟用上下文無關嵌套的typdef。 這些是特性技術所需要的,它允許將元數據添加到類型(特別是諸如指針的內置類型)中,這是編寫STL所必需的。 typename關鍵字與class關鍵字相同。




如果你正在尋找一個更深入的主題的治療,我可以把你變成每個人最喜歡的C ++ hater 。 這個人知道並討厭比我所能想像的更多的C ++。 這同時使FQA令人難以置信的煽動性和優秀的資源。




模板可以放在命名空間中,也可以是類的成員。 宏只是一個預處理步驟。 基本上,模板是語言的第一類成員,與其他所有東西都打得好(更好?)。




雖然模板參數是經過類型檢查的,並且模板比宏有許多優點,但是模板非常像宏,因為它們仍然基於文本替換。 編譯器不會驗證您的模板代碼是否有意義,直到您給它的類型參數替代。 例如,Visual C ++不會抱怨這個函數,只要你不實際調用它:

template<class T>
void Garbage(int a, int b)
{
    fdsa uiofew & (a9 s) fdsahj += *! wtf;
}

因此,通常不可能知道模板代碼是否能夠正確地工作,或者是否能成功編譯,以便模板可以接受的給定的類型參數類別。




這不是答案已經說明的結果。

與那些需要編程的科學家,外科醫生,圖形藝術家和其他人一起工作 - 但不是也不會是專業的全職軟件開發人員 - 我發現偶爾的程序員很容易理解宏,而模板似乎需要更高的抽象思維的水平只有在C ++中深入和持續的編程經驗才有可能。 它需要處理模板是有用的概念的代碼的許多實例,使概念充分地使用。 雖然可以說任何語言功能,模板的經驗量比專業休閒程序員可能從他們的日常工作中獲得的差距更大。

一般的天文學家或電子工程師可能會把宏指望得很好,甚至可以理解為什麼應該避免使用宏,但是對於日常使用來說,模板不夠好。 在這種情況下,宏實際上更好。 當然,還有很多例外。 一些物理學家在專業軟件工程師的周圍運行,但這不是典型的。




這個答案意在說明C預處理器以及它如何用於泛型編程

他們在某種程度上是因為它們使一些類似的語義。 C預處理器已被用來啟用通用數據結構和算法(請參閱令牌Concatination )。 但是,如果不考慮C ++模板的其他功能,它將使整個通用編程遊戲成為一個“ 清理者”來閱讀和實現。

如果任何人想看到硬核C只有通用編程在行動閱讀libevent源代碼 - 這也在這裡提到。 容器/算法的大量集合被實現,並且在單頭文件中完成(非常可讀)。 我真的很佩服這個,C ++模板代碼(我更喜歡它的其他屬性)非常詳細。




答案是這麼長,我不能總結一切,但:

  • 例如,宏在函數模板中並不確保類型安全性:編譯器無法驗證宏參數是否兼容類型 - 也是在函數模板實例化時,編譯器知道intfloat定義的operator +
  • 模板為元編程打開了大門(簡而言之,在編譯時評估事物並做出決定):在編譯時可以知道類型是整數還是浮點數; 無論是一個指針或是否是const限定的,等等... 在即將到來的c ++ 0x中看到“type traits”
  • 類模板有部分專業化
  • 函數模板有明確的完全專業化,在你的例子中add<float>(5, 3); 可以以不同的方式add<int>(5, 3); 這是宏不可能的
  • 宏沒有任何的範圍
  • #define min(i, j) (((i) < (j)) ? (i) : (j)) - 對ij參數進行兩次評估。 例如,如果任一參數具有postincremented變量,則會執行兩次增量
  • 由於宏由預處理器擴展,編譯器錯誤消息將引用擴展宏,而不是宏定義本身。 此外,宏在調試過程中將以擴展形式顯示
  • 等等...

注意:在極少數情況下,我更喜歡依賴於可變宏,因為在c ++ 0x成為主流之前,不存在可變參數模板。 C ++ 11是活的。

參考文獻:




模板提供某種程度的類型安全性。




沒有提到的是模板函數可以推導出參數類型。

template <typename T>
void func(T t)
{
  T make_another = t;

有人可能會爭辯說,即將到來的“typeof”操作符可以解決這個問題,但即使它不能拆分其他模板:

template <typename T>
void func(container<T> c)

甚至:

template <tempate <typename> class Container, typename T>
void func(Container<T> ct)

我也覺得專業化的課題還不夠。 以下是宏不能做的一個簡單例子:

template <typename T>
T min(T a, T B)
{
  return a < b ? a : b;
}

template <>
char* min(char* a, char* b)
{
  if (strcmp(a, b) < 0)
    return a;
  else
    return b;
}

這個空間太小,不能進入專業化類型,但就我而言,你可以用它做什麼,這是令人興奮的。




NO 。 一個簡單的計數器例子:模板遵守命名空間,宏的忽略命名空間(因為它們是預處理器語句)。

namespace foo {
    template <class NumberType>
    NumberType add(NumberType a, NumberType b)
    {
        return a+b;
    }

    #define ADD(x, y) ((x)+(y))
} // namespace foo

namespace logspace 
{
    // no problemo
    template <class NumberType>
    NumberType add(NumberType a, NumberType b)
    {
        return log(a)+log(b);
    }

    // redefintion: warning/error/bugs!
    #define ADD(x, y) (log(x)+log(y))

} // namespace logspace