1. 程式人生 > 實用技巧 >C++ Templates (1.6 但是為什麼不...? But, Should't We ...?)

C++ Templates (1.6 但是為什麼不...? But, Should't We ...?)

返回完整目錄

目錄

1.6 但是為什麼不...? But, Should't We ...?

可能,甚至簡單的函式模板可能引發進一步的疑問,這些疑問可能如此常見,所以在此簡單地進行討論。

1.6.1 傳值還是傳引用? Pass by Value or by Reference?

讀者可能疑惑,為什麼截止目前為止宣告的函式使用傳值方式而非傳引用方式傳遞引數。除了一些拷貝代價小的簡單型別(如基本型別(fundamental types)

或者std::string_view),通常推薦以引用方式進行引數傳遞,因為沒有非必要拷貝被建立。

然而,由於一些原因,值傳遞方式通常更好:

  • 語法更簡單

  • 編譯器更好優化

  • 移動語義(move semantics)使得拷貝成本更低

  • 有時根本沒有拷貝或者移動問題

此外,對於模板來說會摻雜一些特殊的方面:

  • 模板可被用於簡單和複雜的型別,所以選擇適用於複雜型別的方式可能導致簡單型別效率低下(counter-productive)

  • 呼叫者依然可以決定使用值傳遞還是引用傳遞,通過使用std::ref()std::cref(),詳見第7.3節

  • 儘管對於傳遞字串字面值(string literal)

    或者裸陣列(raw arrays)總是問題,以引用方式傳遞他們通常會導致更大的問題。

這些都將在第7章中進行詳細討論。此刻,在本書中通常使用傳值方式進行引數傳遞,除非一些功能只能使用引用。

1.6.2 為什麼不使用inline? Why Not inline?

通常,函式模板不需要宣告為inline。與普通非inline函式不同,非inline函式模板可以定義在標頭檔案中,並且可以在多個解釋單元(translate units)包含此標頭檔案。

該規則的唯一例外是模板針對某些型別的全特化(full specialization),因此得到的程式碼不再是泛型(generic)的(所有的模板引數均已定義)。更多細節參考9.2節。

從一個嚴格的語言定義角度來看,inline僅僅意味著一個函式的定義可以在一個程式中出現多次。然而,這也意味著給編譯器一個提示:對該函式的呼叫應當內聯地展開(expanded inline)。在某些情形下,如此做可以產生更高效的程式碼,但是在其他情形下卻完全相反。當今,在沒有指定inline的情形下,編譯器通常能更好地決定是否內聯展開。然而,在該決策過程中,編譯器依然對出現inline的情形負責(compilers still account for the presence of inline in that decision)。

1.6.3 為什麼不使用constexpr? Why Not constexpr?

自從C++11起,可以使用constexpr來提供在編譯期使用程式碼計算值的能力。對許多模板來說,這非常有意義。

比如,為了在編譯期能夠計算最大值函式,可以這樣宣告:

// basics/maxconstexpr.cpp

template<typename T1, typename T2>
constexpr auto max(T1 a, T2 b)
{
      return b < a ? a : b;
}

該模板函式可以被用於需要編譯期計算的地方,比如當宣告裸陣列的大小:

      int a[::max(sizeof(char), 1000u)];

或者宣告std::array<>的大小:

      std::array<std::string, ::max(sizeof(char), 1000u)> arr;

此處,1000作為unsigned int來避免在模板中“比較有符號和無符號數比較”的警告。

第8.2節將討論使用constexpr的其他例子。然而,為了將注意力放在基本問題上,當討論其他模板特性時,通常會跳過constexpr。