C++范型编程 -- <type_traits>头文件

Posted 远近闻名的学渣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++范型编程 -- <type_traits>头文件相关的知识,希望对你有一定的参考价值。

在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
}

上面的代码中,有一些会根据不同的条件来改变函数的签名,而有的则不会, 具体使用哪种应该看情况使用,比如调整重载候选函数集合时应该主动改变函数签名,而调整虚函数时则不应该改变函数签名

以上是关于C++范型编程 -- <type_traits>头文件的主要内容,如果未能解决你的问题,请参考以下文章

STL标准库 & 范型编程学习笔记:C++学习网站STL六大部件介绍

STL标准库 & 范型编程学习笔记:C++学习网站STL六大部件介绍

STL标准库 & 范型编程学习笔记:OOP(面向对象编程)与 GP(范型编程)容器之间的实现与分类

STL标准库 & 范型编程学习笔记:OOP(面向对象编程)与 GP(范型编程)容器之间的实现与分类

函数模板

STL标准库 & 范型编程学习笔记(10):hashtable深度探索