摘要訊息 : C++ 14 引入了變數樣板, 使得樣板可以用於變數, 函式和類別.
0. 前言
在 C++ 11 中, 如果我們想要獲得某個特性萃取的結果, 我們通常會寫成 constexpr auto value = std::is_signed<int>::value;
. 為了得到更多類似的 value
, 我們可能要對 long
, long long
, short
, signed char
和 char
都進行類似的宣告. 然而對於剩餘的型別呢? 也要一個一個寫出來嗎?
為了解決上面這個需求, C++ 14 的提案 N3651《Variable Templates》提出了變數樣板, 使用帶有樣板的變數來解決這個需求.
更新紀錄 :
- 2022 年 4 月 22 日進行第一次更新和修正.
1. 提案內容
變數樣板的基本形式是
template <template-parameter>
constexpr static T variable-name {initializer-list};
其中, template-parameter
是樣板參數, T
是型別, variable-name
是變數的名稱, {initializer-list}
是初始化列表. 當然, 使用等號或者圓括號進行初始化的方式都是可以的. 另外, constexpr
和 static
是可選的標識.
像 3.1415926
這樣的常數, 在 C++ 中如果沒有任何後綴, 那麼預設是 double
型別. 而實際使用的時候, 它並不一定是 double
型別, 可能是 long double
, float
甚至是用戶自訂型別. 如果沒有辦法確定它的型別, 那麼就可以用 C++ 14 的變數樣板 :
template <typename T>
constexpr T pi {3.1415926};
如果我們需要 float
型別, 那麼我們就可以直接使用 pi<float>
. 在第 0 節中的需求, 我們可以寫成
#include <type_traits>
template <typename T>
constexpr auto value = std::is_signed<T>::value;
C++ 14 同樣為標準樣板程式庫 <type_traits>
引入了變數樣板. 對於超函式內有 value
成員變數的, 都加入了一個變數樣板 _v
. 例如 std::is_signed_v
和 std::is_assignable_v
等. 我們在使用的時候, 就可以不需要加 ::value
了.
對於類別內的 static
變數, 同樣也可以使用變數樣板. 但是如果初始化在類別可視範圍之外, 那麼需要重新宣告樣板參數. 另外, 如果變數型別和樣板參數無關, 也就是變數的型別已知, 那麼變數必須在類別可視範圍內進行初始化 :
#include <type_traits>
struct Foo {
template <typename T>
static const volatile T value;
};
template <typename T>
const volatile T Foo::value {};
struct Bar {
template <typename T>
constexpr static bool value; // Error
template <typename T>
constexpr static bool value {std::is_union<T>::value}; // OK
};
如果閣下熟悉 SFINAE, 那麼需要注意, SFINAE 是不能用於變數樣板中的 :
#include <type_traits>
#include <iostream>
template <int N>
typename std::enable_if<N % 2 == 0, int>::type value {};
template <int N>
typename std::enable_if<N % 2 == 0, int>::type value2 {};
template <int N>
typename std::enable_if<N % 2 == 1, char>::type value2 {'0'}; // redefinition of 'value' with a different type: 'typename std::enable_if<N % 2 == 1, char>::type' vs 'typename std::enable_if<N % 2 == 0, int>::type'
int main(int argc, char *argv[]) {
std::cout << value<0> << std::endl; // 輸出結果 : 0
std::cout << value<1> << std::endl; // Error : failed requirement '1 % 2 == 0'; 'enable_if' cannot be used to disable this declaration
}
一旦編碼器檢測到 std::enable_if
的第一個樣板引數的結果為 false
, 那麼就會立馬擲出編碼錯誤.
自創文章, 原著 : Jonny. 如若閣下需要轉發, 在已經授權的情況下請註明本文出處 :