keywords
- std::enable_if
0 引言
c++ 學(xué)習(xí)筆記 之 c++11 新特性:std::enable_if
1 std::enable_if的概述
std::enable_if,滿足條件時(shí)類型有效。 我們結(jié)合源碼來(lái)看看(位于 type_traits 中)
// Primary template.
/// Define a member typedef @c type only if a boolean constant is true.
template<bool, typename _Tp = void>
struct enable_if
{ };
// Partial specialization for true.
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };
從上面源碼可以看到,只有第一個(gè)模板參數(shù)為true時(shí),struct 才會(huì)定義一個(gè)public的type類型(struct成員默認(rèn)是public類型),type即為第二個(gè)模板參數(shù)
下面看看這幾個(gè)定義
std::enable_if<true, int>::type t; // OK,定義了一個(gè)變量t,類型是int
std::enable_if<false, int>::type t2 // FAIL,因?yàn)闆](méi)有type這個(gè)類型,編譯失敗
std::enable_if<true>::type; // OK, 第一模板參數(shù)是true,第二模板參數(shù)是通常版本中定義的默認(rèn)類型即void
那么std::enable_if 有什么用呢?
2 std::enable_if 的使用場(chǎng)景
參考 cppreference.com std::enable_if 的說(shuō)明文檔,總結(jié)幾個(gè)使用場(chǎng)景如下
2.1 類型偏特化
看看下面案例
關(guān)于偏特化的知識(shí)點(diǎn),后面專門分析
#include <iostream>
template<class T, class Enable = void>
class A {
public:
A() { std::cout << "primary template\r\n"; }
}; // primary template
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
public:
A() { std::cout << "partial specialization\r\n"; }
}; // specialization for floating point types
int main() {
std::shared_ptr<A<int>> a1 = std::make_shared<A<int>>();
auto a2 = std::make_shared<A<double>>();
}
輸出如下
primary template
partial specialization
從上面案例可以看到,可以通過(guò)std::enable_if,控制不同的數(shù)據(jù)類型,選擇不同的類模板
2.2 函數(shù)返回值場(chǎng)景
通過(guò)函數(shù)的返回值,控制不同的條件下,選擇不同的模板。直接看看下面代碼案例
#include <iostream>
// enabled via the return type
template<class T>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type
construct(T *) {
std::cout << "default constructing trivially default constructible T\n";
}
template<class T>
typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type
construct(T *p) {
std::cout << "default constructing non-trivially default constructible T\n";
}
int main() {
int a = 1;
std::string str = "hell";
construct(&a);
construct(&str);
return 0;
}
輸出如下
default constructing trivially default constructible T
default constructing non-trivially default constructible T
2.3 函數(shù)參數(shù)場(chǎng)景
通過(guò)函數(shù)的參數(shù),控制不同的條件下,選擇不同的模板。看看下面案例
#include <iostream>
// enabled via a parameter
template<class T>
void destroy(T *,
typename std::enable_if<std::is_trivially_destructible<T>::value>::type * = 0) {
std::cout << "destroying trivially destructible T\n";
}
// enabled via a non-type template parameter
template<class T, typename std::enable_if<!std::is_trivially_destructible<T>{} &&
(std::is_class<T>{} || std::is_union<T>{}),bool>::type = true>
void destroy(T* t)
{
std::cout << "destroying non-trivially destructible T\n";
}
int main() {
int a = 1;
std::string str = "hell";
destroy(&a);
destroy(&str);
return 0;
}
輸出如下:
destroying trivially destructible T
destroying non-trivially destructible T
上面參考官方文檔列舉了幾個(gè)使用場(chǎng)景。 后面會(huì)根據(jù)實(shí)際應(yīng)用補(bǔ)充場(chǎng)景以及理解。
99 拓展
在官方的介紹文檔里面,提到了SFINAE(Substitution failure is not an error),下面簡(jiǎn)單介紹下這個(gè)概念,先看看下面代碼
#include <iostream>
struct B { typedef int type; };
template<class T>
typename T::type h(typename B::type) { std::cout << "h special\r\n"; }
template<class T>
void h(T t) { std::cout << "h common\r\n"; }
int main() {
h<B>(10);
h<int>(10);
return 0;
}
輸出為
h special
h common
上面就是官方的代碼,我簡(jiǎn)化了下。 從上面輸出看,兩個(gè)函數(shù)調(diào)用都正確推導(dǎo)到了各自的函數(shù)模板。SFINAE 就是如果推導(dǎo)模板函數(shù)可以找到一個(gè)正確的版本,過(guò)程中即使存在模板匹配時(shí)的語(yǔ)法錯(cuò)誤,編譯器也不會(huì)報(bào)錯(cuò),比如上述的 int::type,是存在語(yǔ)法錯(cuò)誤的