範本是由型別、常數值、範本與參數包參數化的宣告族。特化會在 C++ 名稱查找、型別檢查、多載解析與約束規則下形成;範本不是文字替換。
範本參數 #
型別參數接受型別,常數範本參數接受結構化常數值,而範本範本參數接受相容的範本。預設值可提供省略的引數。參數包代表零個或多個參數或引數。
cpp
template <class T, std::size_t N = 16>
struct Buffer;
template <auto Value>
struct Constant;
template <template <class...> class Container, class T>
using one_element_container = Container<T>;
template <class... Ts>
struct TypeList {};類別範本或別名範本可以具有型別、常數、範本與參數包參數。函式範本額外支援推導:出現在可推導參數位置的引數會從呼叫引數推斷,而不可推導參數則必須提供,或從其他地方取得。
cpp
template <class T>
T maximum(T left, T right);
auto a = maximum(1, 2); // T is int
auto b = maximum<double>(1, 2.5);明確的範本引數位於一般呼叫引數之前。推導不會使用函式回傳型別,因為呼叫結果可能被捨棄,而且多載解析需要在得知結果前先取得候選型別。
範本類別與 CTAD #
函式、類別、變數、別名與成員範本具有不同的具現化規則。
cpp
#include <type_traits>
template <class T> T identity(T value) { return value; } // function
template <class T> struct Box { T value; }; // class
template <class T> inline constexpr bool is_box = false; // variable
template <class T> using remove_cvref_t = // alias
std::remove_cvref_t<T>;
struct Store {
template <class T> void put(T&& value); // member
};類別範本引數推導(CTAD)會根據建構函式、聚合推導候選項或使用者定義的推導指南來選擇類別範本引數。當使用類別範本名稱而未指定明確引數時會套用;不適用於函式範本。
cpp
template <class T>
struct Pair { T first; T second; };
template <class T, class U>
Pair(T, U) -> Pair<std::common_type_t<T, U>>;
Pair values{1, 2}; // Pair<int> through the deduction guide
當建構函式參數型別無法表達預期的儲存型別時,請撰寫推導指南。如果在 API 邊界明確寫出型別能更清楚地表達所有權、單位或縮窄策略,請不要依賴 CTAD。
具現化與可見性 #
當需要某個特化時會發生隱含具現化,但仍受具現化點規則約束。相關範本定義必須在該點可達,這也是函式與類別範本定義慣例上位於標頭檔中的原因。
明確具現化定義會產生特化;extern template 宣告會抑制翻譯單元中的隱含具現化,而該翻譯單元可以使用其他地方提供的具現化。
cpp
// widget.hpp
template <class T>
class Widget {
public:
void draw();
};
extern template class Widget<int>;
// widget.cc, after the definition is visible
template <class T>
void Widget<T>::draw() {}
template class Widget<int>;明確具現化是建置時的控制機制,不是讓每個所需特化保持有效的替代方案。請在使用前讓宣告保持可見,並在編譯器必須具現化範本的所有位置讓範本定義保持可見。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)