一、模板原型
template< bool B, class T = void >
struct enable_if;
當B為true時,則std::enable_if擁有等同於T的公開成員typedef type(即enable_if<B,T>::type);否則,無該成員typedef(VS會報錯)
二、示例
void fun(){}
int testFunc()
{
std::enable_if<std::is_function<decltype(fun)>::value, int>::type tt; //這句相當於int tt;
std::enable_if<std::is_function<int>::value, int>::type tt; //報錯,提示enable_if<false,int>沒有成員type
}
三、用途
1. 類型偏特化
在使用模板編程時,經常會用到根據模板參數的某些特性進行不同類型的選擇,或者在編譯時校驗模板參數的某些特性。例如:
template <typename T, typename Enable=void>
struct check;
template <typename T>
struct check<T, typename std::enable_if<T::value>::type> {
static constexpr bool value = T::value;
};
上述的 check 只希望選擇 value==true 的 T,否則就報編譯時錯誤。如果想給用戶更友好的提示,可以提供結構體的原型定義,並在其中進行static_assert的靜態檢查,給出更明確的字符串說明。
2. 控制函數返回類型
對於模板函數,有時希望根據不同的模板參數返回不同類型的值,進而給函數模板也賦予類型模板特化的性質。典型的例子可以參看tuple的獲取第k個元素的get函數:
template <std::size_t k, class T, class... Ts>
typename std::enable_if<k==0, typename element_type_holder<0, T, Ts...>::type&>::type
get(tuple<T, Ts...> &t)
{
return t.tail;
}
template <std::size_t k, class T, class... Ts>
typename std::enable_if<k!=0, typename element_type_holder<k, T, Ts...>::type&>::type
get(tuple<T, Ts...> &t)
{
tuple<Ts...> &base = t;
return get<k-1>(base);
}
由於函數模板不能偏特化,通過 enable_if 便可以根據 k 值的不同情況選擇調用哪個 get,進而實現函數模板的多態。
3. 校驗函數模板參數類型
有時定義的模板函數,只希望特定的類型可以調用,參考cppreference官網示例,很好的說明了如何限制只有整型可以調用的函數定義:
template <typename T>
typename std::enable_if<std::is_const<T>::value&& std::is_integral<T>::value,const int>::type
get(T t)
{ //只有當T的類型為const int時,才可以調用get函數
return t;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
is_odd(T t)
{ //偶數返回false
return bool(t % 2);
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even(T t)
{ //偶數返回true
return !is_odd(t);
}
int main()
{
std::cout<<get<const int>(2);
//std::cout << get<const float>(2.0); //報錯
std::cout << is_odd<int>(2); //false
std::cout << is_even<int>(2); //true
}
一個通過返回值,一個通過默認模板參數,都可以實現校驗模板參數是整型的功能。