[c++] C ++模板是偽裝的宏嗎?



Answers

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

這是MSDN所說的: http://msdn.microsoft.com/en-us/library/aa903548(VS.71).aspxhttp://msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx

以下是宏的一些問題:

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

如果這對你來說還不夠,我不知道是什麼。

Question

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

現在我正在嘗試將模板更深入地集成到我的OO設計中,並且一個嘮叨的想法不斷回到我身邊:它們只是一個宏,真的......你可以使用#defines實現(而不是UGLY)auto_ptrs,如果你真的想要。

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

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

  • 在您不期望它們的地方難以理解的編譯器錯誤?
  • 代碼臃腫?
  • 跟踪代碼有困難嗎?
  • 設置調試器斷點?



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

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



這個答案旨在揭示C預處理器以及它如何用於泛型編程

它們在某些方面是因為它們能夠實現一些類似的語義。 C預處理器已用於啟用通用數據結構和算法(請參閱令牌Concatination )。 但是,如果不考慮C ++模板的任何其他功能,它會使整個通用編程遊戲的閱讀和實現變得更加清晰

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




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

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

因此,對於模板設計為接受的類型參數的給定類別,通常無法知道模板代碼是否能夠正常工作或成功編譯。




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

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;
}

空間太小,無法進入類型專業化,但就我而言,你可以用它做什麼,令人興奮。




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




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

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

普通的天文學家或電子工程師可能很好地理解宏,甚至可以理解為什麼應該避免使用宏,但是不能很好地模擬日常使用的模板。 在這種情況下,宏實際上更好。 當然,存在許多例外情況; 一些物理學家在專業軟件工程師周圍跑圈,但這不典型。




如果您正在尋找對該主題的更深入的處理,我可以將您轉向每個人最喜歡的C ++仇恨 。 這個人知道並且討厭比我夢想的更多的C ++。 這同時使FQA令人難以置信的炎症和優秀的資源。




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

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

注意:在極少數情況下,我更傾向於使用可變參數宏,因為在c ++ 0x成為主流之前,沒有可變參數模板這樣的東西。 C++11現場直播。

參考文獻:




模板了解數據類型。 宏沒有。

這意味著您可以執行以下操作...

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




模板提供一定程度的類型安全性。




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




Links