标签:
在type_traits头文件中定义了许多非常好玩的东西,这里对着 c++ reference 翻译一丢丢
一,helper class , std::intergral_constant
template< class T, T v > struct integral_constant;
我们知道对在模板参数中的非类型参数必须为常量,所以这个东西就是可以为类型T的任意一个常量v,做出来一个特定的类型,即integral_constant<T, v>的实例。通常用来保存int 和 bool,它的实现是这样的:
//声明一个为bool偏特化的模板
template <bool B>
using bool_constant = integral_constant<bool, B>;
//这个偏特化的模板总共就能实例化出两个具体的类型:
typedef std::integral_constant<bool, true> true_type
typedef std::integral_constant<bool, false> false_type
//可能的实现
template<class T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; } //since c++14
};
那么,在模板编程中,计算都是在编译期搞定的, 作为模板参数的非类型参数必须为常量, 这也就说模板元编程中的变量一定是常量。。。如果要改变值只能在搞一个新的常量咯,
这个intergral_constant就能枚举完 T 中的任何一个常量。
二、主要的类型分类,以下的每个类型都有一个value成员,如果判断为真,则为true, 否则为false,这里挑几个简单介绍:
1, is_void, 用法 is_void<T>::value, 判断T是否为void类型, 一看就是用来判断函数返回值类型的
2, is_same<T1,T2> , 用法 is_same<T1, T2>::value, 判断两个类型是否一致,配合typedecl简直不要太爽。。。不过得注意typedecl的引用
。。。。。剩下的略
三、enable_if
原型:
template< bool B, class T = void > struct enable_if;
用法, 如果B为true, 则 enable_if<B, T>会有一个类型成员,名为type, 类型为 T, 配合上面的各种判断,以及SFINAE 特性(无法实例化并不是错误),可以实现花式的自定义重载函数集:
// 1. the return type (bool) is only valid if T is an integral type: template <class T> typename std::enable_if<std::is_integral<T>::value,bool>::type is_odd (T i) {return bool(i%2);} // 2. the second template argument is only valid if T is an integral type: template < class T, class = typename std::enable_if<std::is_integral<T>::value>::type> // 这么写应该更容易明白: class T2 = typename std::enable_if<std::is_integral<T>::value>::type> bool is_even (T i) {return !bool(i%2);} int main() { short int i = 1; // code does not compile if type of i is not integral std::cout << std::boolalpha; std::cout << "i is odd: " << is_odd(i) << std::endl; std::cout << "i is even: " << is_even(i) << std::endl; return 0; }
或者更秀操作的这样:
#include <type_traits> #include <iostream> #include <string> namespace detail { struct inplace_t{}; } void* operator new(std::size_t, void* p, detail::inplace_t) { return p; } // #1, enabled via the return type template<class T,class... Args> typename std::enable_if<std::is_trivially_constructible<T,Args&&...>::value>::type construct(T* t,Args&&... args) { std::cout << "constructing trivially constructible T\n"; } // #2 template<class T, class... Args> std::enable_if_t<!std::is_trivially_constructible<T,Args&&...>::value> //Using helper type construct(T* t,Args&&... args) { std::cout << "constructing non-trivially constructible T\n"; new(t, detail::inplace_t{}) T(args...); } // #3, enabled via a parameter template<class T> void destroy(T* t, typename std::enable_if<std::is_trivially_destructible<T>::value>::type* = 0) { std::cout << "destroying trivially destructible T\n"; } // #4, enabled via a template parameter template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}) >::type* = nullptr> void destroy(T* t) { std::cout << "destroying non-trivially destructible T\n"; t->~T(); } // #5, enabled via a template parameter template<class T, typename = std::enable_if_t<std::is_array<T>::value> > void destroy(T* t) // note, function signature is unmodified { for(std::size_t i = 0; i < std::extent<T>::value; ++i) { destroy((*t)[i]); } } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value> > void destroy(T* t){} // error: has the same signature with #5 */ // the partial specialization of A is enabled via a template parameter template<class T, class Enable = void> class A {}; // primary template template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { }; // specialization for floating point types int main() { std::aligned_union_t<0,int,std::string> u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u),"Hello"); destroy(reinterpret_cast<std::string*>(&u)); A<int> a1; // OK, matches the primary template A<double> a2; // OK, matches the partial specialization }
上面的代码中,有一些会根据不同的条件来改变函数的签名,而有的则不会, 具体使用哪种应该看情况使用,比如调整重载候选函数集合时应该主动改变函数签名,而调整虚函数时则不应该改变函数签名
标签:
原文地址:http://www.cnblogs.com/64open/p/5205558.html