目錄
實現原理
應用場景
條件編譯
通過特化和繼承,實現std::is_xxx系列
思路
舉例
例子1,is_bool
例子2,is_ptr
實現原理
std::true_type/false_type是模板intergral_constant的兩種實現:
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
應用場景
條件編譯
先看一個不使用true_type/false_type,結果編譯失敗的例子。
編程者的用意是通過一個布爾型變量,配合一個模板類型參數T,控制類的行為:
#include <iostream>template<class T, bool b>
class fail
{
public:T a;fail(){if (b){a = 10;std::cout << a << std::endl;}else{a = "10";std::cout << a << std::endl;}}
};
int main(void)
{fail<int, true> f;std::cin.get();return 0;
}
編譯失敗:
誠然,a="10"這個分支是不會在運行時命中的,但是這個分支仍會在編譯時被編譯。而a是個整型(因為fail<int, true>) ,所以給整型賦值字符串將導致編譯失敗。
從上面的分析可以看出,運行時仍然保留了兩個條件分支,是編譯失敗的原因。
現在使用std::true_type/false_type,讓編譯時就命中條件分支,并進行有選擇的編譯,避免語法錯誤。
#include <iostream>
//注:這里使用vs2013,而bool_constant是C++17標準引入的,所以這里手動定義
template<bool val>
using bool_constant = std::integral_constant<bool, val>;template<class T, bool b>
class fail
{
public:T a;fail(){
//注:這里一定要有(),
//否則func函數的輸入參數就是一個類型bool_constant<b>,而不是一個變量
//而類型不能作為輸入參數func(bool_constant<b>());}void func (std::true_type t){a = 10;std::cout << a << std::endl;}void func(std::false_type f){a = "10";std::cout << a << std::endl;}
};
int main(void)
{fail<int, true> f;std::cin.get();return 0;
}
編譯通過,且正常運行:
這里采用的是模板匹配的辦法,沒有直接用true/false命中條件分支,而是先通過true/false實現bool_constant的具體類型(true_type/false_type)。利用類型的區別來匹配合適的func()。
具體實現時,要注意兩點:
1 要用func(bool_constant<b>()),而不是func(bool_constant<b>),因為bool_constant<b>只是個類型,不是變量,不能作為輸入參數;
2 兩個func()函數定義的時候(func(std::true_type), func(std::false_type)),里面還指明了具體的變量名(t,f)。其實也可以不指明變量名,因為這兩個變量在函數體內沒有用到,也不會因為沒有變量名就影響匹配。
通過特化和繼承,實現std::is_xxx系列
思路
先假定被判斷的類型“不是”定義一個模板,既然不是,就繼承false_type,然后再特化之,定義“是”的情況,既然是,就繼承true_type。
舉例
例子1,is_bool
#include <iostream>template<class T>
struct is_bool : std::false_type
{};template<>
struct is_bool<bool> : std::true_type
{};int main(void)
{std::cout << is_bool<int>() << std::endl;std::cout << is_bool<bool>() << std::endl;std::cin.get();return 0;
}
結果:
?至于為什么cout可以直接輸出is_bool<int/bool>(),它又不是一個簡單的布爾型,而是一個自定義的結構體?那是因為類型轉換函數的結果,參見類型轉換運算符(conversion operator)-CSDN博客
例子2,is_ptr
代碼:
#include <iostream>template<class T>
struct is_ptr : std::false_type
{};template<class T>
struct is_ptr<T *> : std::true_type
{};int main(void)
{std::cout << is_ptr<int>() << std::endl;std::cout << is_ptr<int *>() << std::endl;std::cin.get();return 0;
}
結果:
關于模板如何匹配到指針,那是因為模板匹配的原則是匹配與當前情況最貼切的模板。參看我前面的博客初嘗類型萃取--typename、模板偏特化、和traits之(二)模板偏特化-CSDN博客