關於<type_traits>
<type_traits>
是C++自2011年標準後添加到STL中的一個頭文件,正如其名,它提供了一系列模板類去確定類型的屬性,例如:
// 檢查傳入類型參數是否爲void
template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
// 可以這樣使用它
#include <type_traits>
bool _v_flag = std::is_void<int>::value; // false
bool _i_flag = std::is_void<void>::value; // true
更多具體示例見這裏,本文以is_void<>
爲例,簡單剖析一下<type_traits>
對c++內建類型的實現方法
那麼爲什麼不講一下對自定義類型的實現呢?因爲C++在編譯時無法產生太多的元信息,因此無法進行模板替換與展開,這部分代碼實現是直接寫在編譯器源碼中的,而我又比較懶,不想去翻gcc的源碼……
is_void<>
模板展開
首先,is_void<>
是一個模板類,只不過這個模板類是由struct
定義的——除了成員訪問權限默認爲public
,與class
並無二致——它的源碼在文章開頭已經貼過了,這裏再貼一遍:
template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
可以看到,它以公開繼承的方式繼承了另外一個模板類__is_void_helper
,看來這個類模板是我們下一步要找的目標,不過先看一下這個繼承中的其他部分再去考慮這個基類的實現
其中remove_cv
也是聲明在該文件中一個類模板,但是在該文件中僅有聲明而無實現,估計也是由編譯器實現的吧,它的作用是抹去傳入類型的const/volatile
屬性,例如傳入類型參數爲const void
,那麼其類型成員type
將被指定爲void
,這樣被繼承的基類部分就變成了__is_void_helper<void>::type
下面來看__is_void_helper<>
的實現:
template<typename>
struct __is_void_helper : public false_type { };
template<>
struct __is_void_helper<void> : public true_type { }
這裏可以看到,根據傳入類型的不同,__is_void_helper<>
具有兩條不同的繼承路線——傳入void
就繼承true_type
,否則繼承false_type
,那麼下面繼續看這兩個不同基類的實現:
/// The type used as a compile-time boolean with true value.
typedef integral_constant<bool, true> true_type;
/// The type used as a compile-time boolean with false value.
typedef integral_constant<bool, false> false_type;
顯然這兩個基類是對同一個類型不同參數的類型重定義,那麼找到這個intergral_constant<typename _Tp, _Tp __v>
至關重要:
template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
constexpr operator value_type() const noexcept { return value; }
}
這個時候就一目瞭然了,根據is_void<>
傳入的類型參數不同,最後繼承的integral_constant
也不同,整個模板的展開過程大致如下圖所示:
新標準中的<type_traits>
在C++14/17中,對該頭文件進行了多處修改,其中比較重要的有兩處:
-
C++14中,對
integral_constant
模板添加了operator()
的重載:constexpr value_type operator() const noexcept { return value; }
這意味着可以將該模板作爲函數對象(functional object)傳遞,也可以用這樣的方式獲取類型判斷的結果:
bool _v_flag = std::is_void<void>(); // true
-
C++17中,添加了一種直接獲取
integral_constant::value
的模板變量,它具有這樣的通用命名方式is_type_v<>
針對上例,可以這樣獲取類型判斷的結果:bool _v_flag = std::is_void_v<void>; // true
但是要注意,在目前主流編譯器(GCC/MSVC)中默認使用的C++標準似乎是C++14從而導致編譯失敗,針對GCC可以在編譯命令中添加
--std=c++17
或者--std=c++2a
解決這個問題