精华内容
下载资源
问答
  • type_traits

    千次阅读 2019-07-29 18:31:00
    type_traits提供了丰富的编译期间计算、查询、判断、转换和选择的帮助类,其被定义在#include <type_traits>下。 作用: 增强了泛型编程能力; 增强程序的弹性,使得在编译期间就可以做到优化、改进甚至...

     

    概述

    type_traits提供了丰富的编译期间计算、查询、判断、转换和选择的帮助类,其被定义在#include <type_traits>下。

    作用:

    • 增强了泛型编程能力;
    • 增强程序的弹性,使得在编译期间就可以做到优化、改进甚至排错,提高代码质量;
    • 它所提供的选择功能一定程度上可以消除冗长的if-else或switch-case语句,降低程序的圈复杂度,提高代码的可维护性;
    • 它所提供的判断功能,在编译期间可以检查出是否是正确的类型,以便能编写更为安全的代码。

     

    • Helper Class

    作用: 帮助创建编译时常量的标准类。

    在C++11之前我们定义编译期常量的方法:

    1.  通过定义静态变量,并通过CompileTimeContents::value方式获取该常量。

        template<typename Type>
        struct CompileTimeContents{
            static const int value = 1;
        };

    2.  通过定义枚举变量方式。

        template<typename Type>
        struct CompileTimeContents{
           enum {value = 1};
        };

    但在C++11中可以通过std::integral_constant 模版类派生:

        template<typename Type>
        struct CompileTimeContents :std::integral_constant<int, 1>
        {
        };

    通过CompileTimeContents<type_name>::value方式获取该常量,这种写法好处是不用再定义额外变量,使用起来更加方便。

    在标准库中的定义:

        template <class T, T v>
        struct integral_constant {
          static constexpr T value = v;
          typedef T value_type;
          typedef integral_constant<T,v> type;
          constexpr operator T() { return v; }
        };

     

    • 判断类型

    在traits中判断类型的模板类 都 继承自 std::integral_constant

    1.  判断目标类型是否为期望类型。

    traits判断类型(下表展示部分,其他可到 http://www.cplusplus.com/reference/type_traits/ 查看):

    traits类型说明

    is_array

    判断是否为数组类型

    is_class

    判断是否为类类型而不是union类型

    is_function

    判断是否为函数类型

    is_reference

    判断是否为引用类型

    is_pod

    判断是否为POD(传统Cstruct类型)

    is_trivial

    判断是否为内置类型

    使用实例:

    // is_array example
    #include <iostream>
    #include <array>
    #include <string>
    #include <type_traits>
    
    int main() {
      std::cout << std::boolalpha;
      std::cout << "is_array:" << std::endl;
      std::cout << "int: " << std::is_array<int>::value << std::endl;
      std::cout << "int[3]: " << std::is_array<int[3]>::value << std::endl;
      std::cout << "array<int,3>: " << std::is_array<std::array<int,3>>::value << std::endl;
      std::cout << "string: " << std::is_array<std::string>::value << std::endl;
      std::cout << "string[3]: " << std::is_array<std::string[3]>::value << std::endl;
      return 0;
    }

    运行结果:

    判断类型通常会与std::enable_if结合使用,通过SFINAE(替换非错误)特性来实现功能更强的重载。

     

    2.  判断两个模板类型之间的关系。

    traits类型说明
    is_same判断两个类是否相同
    is_base_of判断Base类型是否为Derivedl 类型的基类
    is_convertible判断前面模板参数类型能否转换为后面模板参数类型

     具体用法在官网依然有示例:

    // is_base_of example
    #include <iostream>
    #include <type_traits>
    
    struct A {};
    struct B : A {};
    
    int main() {
      std::cout << std::boolalpha;
      std::cout << "is_base_of:" << std::endl;
      std::cout << "int, int: " << std::is_base_of<int,int>::value << std::endl;
      std::cout << "A, A: " << std::is_base_of<A,A>::value << std::endl;
      std::cout << "A, B: " << std::is_base_of<A,B>::value << std::endl;
      std::cout << "A, const B: " << std::is_base_of<A,const B>::value << std::endl;
      std::cout << "A&, B&: " << std::is_base_of<A&,B&>::value << std::endl;
      std::cout << "B, A: " << std::is_base_of<B,A>::value << std::endl;
      return 0;
    }

    运行结果:

     

    • 类型转换

    traits中给出了对参数属性的修改接口,如:其cv属性额添加或移除、引用的添加或移除、数组维度的修改等。

    traits类型说明
    remove_cv移除cv属性
    add_cv添加cv属性
    remove_reference移除引用
    add_lvaue_reference添加左值引用
    remove_extent移除数组顶层维度

    示例:

    // remove_cv example
    #include <iostream>
    #include <type_traits>
    
    int main() {
      typedef const volatile char cvchar;
      std::remove_cv<cvchar>::type a;       // char a
      std::remove_cv<char* const>::type b;  // char* b
      std::remove_cv<const char*>::type c;  // const char* c (no changes)
    
      if (std::is_const<decltype(a)>::value)
        std::cout << "type of a is const" << std::endl;
      else
        std::cout << "type of a is not const" << std::endl;
    
      if (std::is_volatile<decltype(a)>::value)
        std::cout << "type of a is volatile" << std::endl;
      else
        std::cout << "type of a is not volatile" << std::endl;
    
      return 0;
    }

    运行结果:

     

    • 根据条件选择traits

    std::conditional 在编译期间根据判断式选择两个类型中的一个,类似于常用的条件表达式。

    template <bool Cond, class T, class F> struct conditional;

              当条件为真返回T类型,条件为假返回F类型。

    例如:比较long long类型与long double 返回较大的类型

    #include <iostream>
    #include <typeinfo>
    #include <type_traits>
    
    int main() {
    	typedef std::conditional<(sizeof(long long) > sizeof(long double)), \
    		long long, long double > ::type max_size_t;
    	std::cout << typeid(max_size_t).name() << std::endl;
    
    	return 0;
    }

    运行结果:

     

    • 可调用对象返回值类型

    通常我们获取函数返回值的类型使用的是decltype方式:

    template<typename T, typename Arg>
    auto Func(T a, Arg arg)->decltype(f(arg))
    {
        return f(arg);
    }

    但是在某个类型没有模板参数时,就不能通过decltype来获取类型

    #include <iostream>
    #include <type_traits>
    
    class A{
    	A() = delete;
    
    public:
    	int operator()(int i){
    		return i;
    	}
    };
    
    int main() {
    	// decltype(A()(0)) i = 4;
    	decltype(std::declval<A>()(std::declval<int>())) i = 4;
    	
    	std::cout << typeid(decltype(std::declval<A>()(std::declval<int>()))).name() << std::endl;
    	std::cout << i << std::endl;
    
    	return 0;
    }

    当用仅用decltype方式获取类型时,由于A没有默认构造函数,代码出错。

    之后采用declval方式,通过 declval<A>() 获取任意类型的临时值,但临时值不能用于求值,需要借助decltype对临时值进行类型推导,得出最终返回值。

    在traits中提供了另一解决途径:std::result_of

    	std::result_of<A(int)>::type i = 4;
    

    函数原型: 

        // 第一个模板参数为可调用对象类型,第二个模板参数为参数类型
        template <class Fn, class... ArgTypes> 
        struct result_of<Fn(ArgTypes...)>;

     

    • ​​​​​​​根据条件禁用或启用某些类型

    编译器在匹配重载函数时,通常匹配所有的重载函数,匹配一个如果失败了,编译器不会报错,而是接着匹配其他重载函数,选择最精确的一个去执行,整个过程不会报错(SFINAE)。

    tratis中std::enable_if 根据限定条件选择重载函数,只对满足条件的函数有效。可作用于返回值、模板定义、类模板特化、参数类型限定。

    函数原型:

        template <bool Cond, class T = void> 
        struct enable_if;

    用法示例:

    #include <iostream>
    #include <type_traits>
    
    // 1. 对函数返回值限定,只有模板参数T是integral类型时,才执行此函数
    template <class T>
    typename std::enable_if<std::is_integral<T>::value, bool>::type
    is_odd(T i) { return bool(i % 2); }
    
    
    // 2. 对模板参数限定,模板特化时,模板参数只能是intergal类型
    template < class T,
    class = 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;
    }

    且通过编译期检查输入模板参数是否有效,来提前显示编译错误,避免运行时才被发现。

    ​​​​​​​

    展开全文
  • C++11中的头文件type_traits定义了一系列模板类,在编译期获得某一参数、某一变量、某一个类等等类型信息,主要做静态检查。 此头文件包含三部分: (1).Helper类:帮助创建编译时常量的标准模板类。介绍见以下测试...

    C++11中的头文件type_traits定义了一系列模板类,在编译期获得某一参数、某一变量、某一个类等等类型信息,主要做静态检查

    此头文件包含三部分:

    (1).Helper类:帮助创建编译时常量的标准模板类。介绍见以下测试代码:

    template <unsigned n>
    struct factorial : std::integral_constant<int, n * factorial<n - 1>::value> {};
    
    template <>
    struct factorial<0> : std::integral_constant<int, 1> {};
    
    constexpr unsigned test_integral_constant() noexcept { return factorial<5>::value; }
    static_assert(test_integral_constant() == 120, "value should be 120");
    
    // reference: https://stackoverflow.com/questions/58694521/what-is-stdfalse-type-or-stdtrue-type
    struct true_type {
    	static constexpr bool value = true;
    	constexpr operator bool() const noexcept { return value; }
    	// ...
    };
    
    struct false_type {
    	static constexpr bool value = false;
    	constexpr operator bool() const noexcept { return value; }
    	// ...
    };
    
    int test_type_traits_helper_classes()
    {
    	// std::integral_constant: 编译时将常量作为类型,一般用做trait type的基类
    	static_assert(factorial<5>::value == 120, "value should be 120");
    	fprintf(stdout, "result: %u\n", factorial<5>::value); // constexpr(no calculations on runtime)
    
    	// std::true_type: ==> std::integral_constant<bool, true>, 实例化integral_constant用以表示布尔值true
    	static constexpr bool value1 = true;
    	static_assert(std::integral_constant<bool, value1>::value == true, "value should be true");
    
    	// std::false_type: ==> std::integral_constant<bool, false>, 实例化integral_constant用以表示布尔值false
    	static constexpr bool value2 = false;
    	static_assert(std::integral_constant<bool, value2>::value == false, "value should be false");
    
    	return 0;
    }

    (2).类型特征(Type traits):以编译时常量值的形式获取类型特征(characteristics of types)的模板类。介绍见以下测试代码:

    int func(int i) { return i; }
    class A { public: void func() {} };
    struct B { int func(int a) { return a; } };
    union C { int i; float f; };
    enum class D { x, y, z };
    struct F { int a; };
    struct G { virtual void func() = 0; };
    struct H { virtual void func() {} };
    struct I : G { void func() override {} };
    struct J { ~J() {} };
    struct K { int x; K(const K& k) : x(k.x){}; };
    struct L { virtual ~L() {} };
    struct M { M& operator=(M&&) = delete; };
    struct N { N& operator=(const A&) { return *this; } };
    struct P { P(int) {} };
    struct Q { ~Q() = delete; };
    struct R : B { };
    struct S {};
    
    int test_type_traits_type_traits()
    {
    	// 1.Primary type categories
    	// std::is_array: T是不是数组类型,注意:此函数不会将数组类模板的实例化视为数组类型
    	static_assert(std::is_array<A>::value == false, "");
    	static_assert(std::is_array<A[3]>::value == true, "");
    	static_assert(std::is_array<std::array<int, 3>>::value == false, "");
    
    	// std::is_class: T是不是类类型
    	static_assert(std::is_class<A>::value == true, "");
    	static_assert(std::is_class<B>::value == true, "");
    	static_assert(std::is_class<int>::value == false, "");
    
    	// std::is_enum: T是不是枚举类型
    	static_assert(std::is_enum<D>::value == true, "");
    	static_assert(std::is_enum<A>::value == false, "");
    
    	// std::is_floating_point: T是不是浮点类型
    	static_assert(std::is_floating_point<float>::value == true, "");
    	static_assert(std::is_floating_point<float&>::value == false, "");
    	static_assert(std::is_floating_point<const double>::value == true, "");
    
    	// std::is_function: T是不是函数类型
    	static_assert(std::is_function<decltype(func)>::value == true, "");
    	static_assert(std::is_function<int(int)>::value == true, "");
    
    	// std::is_integral: T是不是整数类型
    	static_assert(std::is_integral<const int>::value == true, "");
    	static_assert(std::is_integral<bool>::value == true, "");
    	static_assert(std::is_integral<char>::value == true, "");
    
    	// std::is_lvalue_reference: T是不是左值引用类型
    	static_assert(std::is_lvalue_reference<const A&>::value == true, "");
    	static_assert(std::is_lvalue_reference<const A&&>::value == false, "");
    
    	// std::is_rvalue_reference: T是不是右值引用类型
    	static_assert(std::is_rvalue_reference<const A&>::value == false, "");
    	static_assert(std::is_rvalue_reference<const A&&>::value == true, "");
    
    	// std::is_member_function_pointer: T是不是非静态成员函数指针类型
    	void(A::*pt)() = &A::func;
    	static_assert(std::is_member_function_pointer<decltype(pt)>::value == true, "");
    	static_assert(std::is_member_function_pointer<void(B::*)()>::value == true, "");
    	static_assert(std::is_member_function_pointer<A*>::value == false, "");
    
    	// std::is_member_object_pointer: T是不是非静态成员数据指针类型
    	int F::* pt2 = &F::a;
    	static_assert(std::is_member_object_pointer<decltype(pt2)>::value == true, "");
    	static_assert(std::is_member_object_pointer<int F::*>::value == true, "");
    
    	// std::is_pointer: T是不是指针类型
    	static_assert(std::is_pointer<int*>::value == true, "");
    	static_assert(std::is_pointer<int**>::value == true, "");
    	static_assert(std::is_pointer<int(*)(int)>::value == true, "");
    
    	//  std::is_union: T是不是联合体类型
    	static_assert(std::is_union<C>::value == true, "");
    	static_assert(std::is_union<A>::value == false, "");
    
    	// std::is_void: T是不是void类型
    	static_assert(std::is_void<void>::value == true, "");
    	static_assert(std::is_void<A>::value == false, "");
    
    	// 2.Composite type categories: 复合类型
    	// std::is_arithmetic: T是不是算术类型
    	static_assert(std::is_arithmetic<char>::value == true, "");
    	static_assert(std::is_arithmetic<char*>::value == false, "");
    
    	// std::is_compound: T是不是复合类型,即不是基础类型
    	static_assert(std::is_compound<char>::value == false, "");
    	static_assert(std::is_compound<char*>::value == true, "");
    	static_assert(std::is_compound<char&>::value == true, "");
    
    	// std::is_fundamental: T是不是基础类型
    	static_assert(std::is_fundamental<char>::value == true, "");
    	static_assert(std::is_fundamental<char*>::value == false, "");
    	static_assert(std::is_fundamental<void>::value == true, "");
    
    	// std::is_member_pointer: T是不是非静态成员指针类型
    	int F::* pt3 = &F::a;
    	static_assert(std::is_member_pointer<int F::*>::value == true, "");
    	static_assert(std::is_member_pointer<decltype(pt3)>::value == true, "");
    	static_assert(std::is_member_pointer<void(B::*)()>::value == true, "");
    
    	// std::is_object: T是不是对象类型,除函数、引用、void之外的所有类型
    	static_assert(std::is_object<float>::value == true, "");
    	static_assert(std::is_object<float&>::value == false, "");
    	static_assert(std::is_object<int(int)>::value == false, "");
    	static_assert(std::is_object<int(*)(int)>::value == true, "");
    
    	// std::is_reference: T是不是引用类型,左值引用或右值引用
    	static_assert(std::is_reference<int&>::value == true, "");
    	static_assert(std::is_reference<int&&>::value == true, "");
    
    	// std::is_scalar: T是不是标量类型,基础类型
    	static_assert(std::is_scalar<int&>::value == false, "");
    	static_assert(std::is_scalar<int*>::value == true, "");
    	static_assert(std::is_scalar<A>::value == false, "");
    
    	// 3.Type properties
    	// std::is_abstract: T是不是抽象类
    	static_assert(std::is_abstract<G>::value == true, "");
    	static_assert(std::is_abstract<H>::value == false, "");
    	static_assert(std::is_abstract<I>::value == false, "");
    
    	// std::is_const: T是不是const限定类型
    	static_assert(std::is_const<const int>::value == true, "");
    	static_assert(std::is_const<const int*>::value == false, "");
    	static_assert(std::is_const<int* const>::value == true, "");
    
    	// std::is_empty: T是不是空类,空类是不存储任何数据的类,即没有非静态数据成员、没有虚函数、也没有虚基类
    	static_assert(std::is_empty<F>::value == false, "");
    	static_assert(std::is_empty<A>::value == true, "");
    	static_assert(std::is_empty<H>::value == false, "");
    
    	// std::is_literal_type: T是不是literal类型,可视为constexpr类型,标量、引用、certain 类,以及这些类型的数组都是literal类型
    	static_assert(std::is_literal_type<A>::value == true, "");
    	static_assert(std::is_literal_type<D>::value == true, "");
    	static_assert(std::is_literal_type<J>::value == false, "");
    
    	// std::is_pod: T是不是POD(Plain Old Data)类型,C语言支持的数据类型,若是类,它需要是trial(普通的)和standard-layout
    	static_assert(std::is_pod<A>::value == true, "");
    	static_assert(std::is_pod<G>::value == false, "");
    	static_assert(std::is_pod<J>::value == false, "");
    
    	// std::is_polymorphic: T是不是多态类
    	static_assert(std::is_polymorphic<A>::value == false, "");
    	static_assert(std::is_polymorphic<G>::value == true, "");
    	static_assert(std::is_polymorphic<I>::value == true, "");
    
    	// std::is_signed: T是不是有符号算术类型
    	static_assert(std::is_signed<A>::value == false, "");
    	static_assert(std::is_signed<char>::value == true, "");
    	static_assert(std::is_signed<int&>::value == false, "");
    
    	// std::is_standard_layout: T是不是standard layout类型,如标量;若是类,则要求没有虚函数、虚基类、
    	// 所有的非静态数据成员都具有相同的访问权限、在派生类中没有非静态数据成员
    	static_assert(std::is_standard_layout<int>::value == true, "");
    	static_assert(std::is_standard_layout<C>::value == true, "");
    	static_assert(std::is_standard_layout<H>::value == false, "");
    
    	// std::is_trivial: T是不是trivial(普通的)类型,如标量,存储连续;若是类,要求是默认构造/拷贝/移动拷贝/析构,没有虚成员
    	static_assert(std::is_trivial<float>::value == true, "");
    	static_assert(std::is_trivial<C>::value == true, "");
    	static_assert(std::is_trivial<F>::value == true, "");
    
    	// std::is_trivially_copyable: T是不是普通的拷贝类型,如标量;若是类,要求使用隐式定义的拷贝/移动函数、析构函数,没有虚成员
    	#ifdef _MSC_VER
    		static_assert(std::is_trivially_copyable<unsigned int>::value == true, "");
    		static_assert(std::is_trivially_copyable<C>::value == true, "");
    		static_assert(std::is_trivially_copyable<K>::value == false, "");
    	#endif
    
    	// std::is_unsigned: T是不是无符号算术类型
    	static_assert(std::is_unsigned<unsigned int>::value == true, "");
    	static_assert(std::is_unsigned<double>::value == false, "");
    
    	// std::is_volatile: T是不是volatile-qualified类型
    	static_assert(std::is_volatile<unsigned int>::value == false, "");
    	static_assert(std::is_volatile<volatile int>::value == true, "");
    	static_assert(std::is_volatile<volatile int*>::value == false, "");
    	static_assert(std::is_volatile<int* volatile>::value == true, "");
    
    	// 4.Type features
    	// std::has_virtual_destructor: T是不是具有虚析构函数的类
    	static_assert(std::has_virtual_destructor<A>::value == false, "");
    	static_assert(std::has_virtual_destructor<L>::value == true, "");
    
    	// std::is_assignable: U是不是赋值给T的类型; std::is_copy_assignable/std::is_move_assignable: T是不是拷贝/移动赋值类型
    	static_assert(std::is_assignable<N, A>::value == true, "");
    	static_assert(std::is_assignable<A, N>::value == false, "");
    	static_assert(std::is_copy_assignable<int>::value == true, "");
    	static_assert(std::is_copy_assignable<L>::value == true, "");
    	static_assert(std::is_copy_assignable<M>::value == false, "");
    	static_assert(std::is_move_assignable<L>::value == true, "");
    	static_assert(std::is_move_assignable<M>::value == false, "");
    
    	// std::is_constructible: 使用指定的参数,T是不是可构造类型
    	// std::is_copy_constructible/std::is_default_constructible/std::is_move_constructible: T是不是拷贝构造/默认构造/移动构造类型
    	static_assert(std::is_constructible<int>::value == true, "");
    	static_assert(std::is_constructible<P, int>::value == true, "");
    	static_assert(std::is_constructible<P, int, int>::value == false, "");
    	static_assert(std::is_copy_constructible<M>::value == false, "");
    	static_assert(std::is_copy_constructible<P>::value == true, "");
    	static_assert(std::is_default_constructible<P>::value == false, "");
    	static_assert(std::is_default_constructible<L>::value == true, "");
    	static_assert(std::is_move_constructible<L>::value == true, "");
    	static_assert(std::is_move_constructible<M>::value == false, "");
    
    	// std::is_destructible: T是不是destructible类型,其析构函数不会被删除,且在派生类中是可访问的
    	static_assert(std::is_destructible<int>::value == true, "");
    	static_assert(std::is_destructible<L>::value == true, "");
    	static_assert(std::is_destructible<Q>::value == false, "");
    
    	#ifdef _MSC_VER
    		static_assert(std::is_trivially_assignable<F, F>::value == true, "");
    		static_assert(std::is_trivially_constructible<F>::value == true, "");
    		static_assert(std::is_trivially_destructible<J>::value == false, "");
    	#endif
    
    	static_assert(std::is_nothrow_assignable<F, F>::value == true, "");
    	static_assert(std::is_nothrow_constructible<F>::value == true, "");
    	static_assert(std::is_nothrow_destructible<J>::value == true, "");
    
    	// 5.Type relationships
    	// std::is_base_of: 判断基类是不是派生类的基类
    	static_assert(std::is_base_of<int, int>::value == false, "");
    	static_assert(std::is_base_of<G, I>::value == true, "");
    	static_assert(std::is_base_of<I, G>::value == false, "");
    
    	// std::is_convertible: 判断From是不是可以隐式转换到To
    	static_assert(std::is_convertible<int, double>::value == true, "");
    	static_assert(std::is_convertible<B, R>::value == false, "");
    	static_assert(std::is_convertible<R, B>::value == true, "");
    
    	// std::is_same: 判断U和T是不是属于相同的类型,当且仅当一个是另一种的typedef时,才认为两个不同的类型名代表相同的类型
    	static_assert(std::is_same<int, const int>::value == false, "");
    	typedef int integer_type;
    	static_assert(std::is_same<int, integer_type>::value == true, "");
    	typedef R R1;
    	static_assert(std::is_same<R1, R>::value == true, "");
    
    	// 6.Property queries
    	// std::alignment_of: 返回类型T的对齐值
    	static_assert(std::alignment_of<int>::value == 4, "");
    	static_assert(std::alignment_of<S>::value == 1, "");
    	static_assert(std::alignment_of<H*>::value == 8, "");
    
    	// std::extent: 获取类型T第i维的范围
    	typedef int mytype[][24][60];
    	static_assert(std::extent<mytype, 0>::value == 0, "");
    	static_assert(std::extent<mytype, 1>::value == 24, "");
    	static_assert(std::extent<mytype, 2>::value == 60, "");
    	static_assert(std::extent<mytype, 3>::value == 0, "");
    
    	return 0;
    }

    (3).类型转换(Type transformations):通过对现有类型进行特定的转换来获取新类型的模板类。介绍见以下测试代码:

    int test_type_traits_type_transformations()
    {
    	// 1.Const-volatile qualifications
    	// std::add_const/std::remove_const: 对类型T添加/移除const限定符
    	typedef std::add_const<int>::type Ax;         // const int
    	typedef std::add_const<const int>::type Bx;   // const int(unchanged)
    	typedef std::add_const<const int*>::type Cx;  // const int* const
    	typedef std::add_const<int* const>::type Dx;  // int* const(unchanged)
    	typedef std::add_const<const int&>::type Ex;  // const int&(unchanged
    
    	static_assert(std::is_const<Ax>::value == true, "");
    	static_assert(std::is_const<Bx>::value == true, "");
    	static_assert(std::is_const<Cx>::value == true, "");
    	static_assert(std::is_const<Dx>::value == true, "");
    	static_assert(std::is_const<Ex>::value == false, "");
    
    	typedef std::remove_const<const char>::type Fx;
    	static_assert(std::is_same<char, Fx>::value == true, "");
    
    	// std::add_cv/std::remove_cv: 对类型T添加/移除const volatile限定符
    	typedef std::add_cv<int>::type Ay;
    	typedef std::add_cv<const int>::type By;
    	typedef std::add_cv<volatile int>::type Cy;
    	typedef std::add_cv<const volatile int>::type Dy;
    
    	static_assert(std::is_same<const volatile int, Ay>::value == true, "");
    	static_assert(std::is_same<const volatile int, By>::value == true, "");
    	static_assert(std::is_same<const volatile int, Cy>::value == true, "");
    	static_assert(std::is_same<const volatile int, Dy>::value == true, "");
    
    	typedef std::remove_cv<const volatile char>::type Fy;
    	static_assert(std::is_same<char, Fy>::value == true, "");
    
    	// std::add_volatile/std::remove_volatile: 对类型T添加/移除volatile限定符
    	typedef std::add_volatile<int>::type Az;            // volatile int
    	typedef std::add_volatile<volatile int>::type Bz;   // volatile int(unchanged)
    	typedef std::add_volatile<int* volatile>::type Cz;  // int* volatile(unchanged)
    	typedef std::add_volatile<volatile int*>::type Dz;  // volatile int* volatile
    	typedef std::add_volatile<volatile int&>::type Ez;  // volatile int&(unchanged)
    
    	static_assert(std::is_volatile<Az>::value == true, "");
    	static_assert(std::is_volatile<Bz>::value == true, "");
    	static_assert(std::is_volatile<Cz>::value == true, "");
    	static_assert(std::is_volatile<Dz>::value == true, "");
    	static_assert(std::is_volatile<Ez>::value == false, "");
    
    	typedef std::remove_volatile<volatile char>::type Fz;
    	static_assert(std::is_same<char, Fz>::value == true, "");
    
    	// 2.Compound type alterations
    	// std::add_pointer/std::remove_pointer: 获取/移除T的指针类型
    	typedef std::add_pointer<int>::type Ap;        // int*
    	typedef std::add_pointer<const int>::type Bp;  // const int*
    	typedef std::add_pointer<int&>::type Cp;       // int*
    	typedef std::add_pointer<int*>::type Dp;       // int**
    	typedef std::add_pointer<int(int)>::type Ep;   // int(*)(int)
    
    	static_assert(std::is_same<int*, Ap>::value == true, "");
    	static_assert(std::is_same<const int*, Bp>::value == true, "");
    	static_assert(std::is_same<int*, Cp>::value == true, "");
    	static_assert(std::is_same<int**, Dp>::value == true, "");
    	static_assert(std::is_same<int*, Ep>::value == false, "");
    
    	typedef std::remove_pointer<int**>::type Fp;
    	static_assert(std::is_same<int*, Fp>::value == true, "");
    
    	// std::add_lvalue_reference/std::add_rvalue_reference:获取T左值/右值引用类型;std::remove_reference: 获取T非引用类型
    	typedef std::add_lvalue_reference<int>::type Aq;    // int&
    	typedef std::add_lvalue_reference<int&>::type Bq;   // int&
    	typedef std::add_lvalue_reference<int&&>::type Cq;  // int&
    	typedef std::add_lvalue_reference<int*>::type Dq;   // int*&
    
    	static_assert(std::is_same<int&, Aq>::value == true, "");
    	static_assert(std::is_same<int&, Bq>::value == true, "");
    	static_assert(std::is_same<int&, Cq>::value == true, "");
    	static_assert(std::is_same<int&, Dq>::value == false, "");
    
    	typedef std::add_rvalue_reference<int>::type Ar;    // int&&
    	typedef std::add_rvalue_reference<int&>::type Br;   // int&  (no change)
    	typedef std::add_rvalue_reference<int&&>::type Cr;  // int&& (no change)
    	typedef std::add_rvalue_reference<int*>::type Dr;   // int*&&
    
    	static_assert(std::is_same<int&&, Ar>::value == true, "");
    	static_assert(std::is_same<int&&, Br>::value == false, "");
    	static_assert(std::is_same<int&&, Cr>::value == true, "");
    	static_assert(std::is_same<int&&, Dr>::value == false, "");
    
    	typedef std::remove_reference<int&>::type Fq;
    	typedef std::remove_reference<int&&>::type Fr;
    	static_assert(std::is_same<int, Fq>::value == true, "");
    	static_assert(std::is_same<int, Fr>::value == true, "");
    
    	// std::decay: 获得T的decay(退化)类型
    	typedef std::decay<int>::type As;           // int
    	typedef std::decay<int&>::type Bs;          // int
    	typedef std::decay<int&&>::type Cs;         // int
    	typedef std::decay<const int&>::type Ds;    // int
    	typedef std::decay<int[2]>::type Es;        // int*
    	typedef std::decay<int(int)>::type Fs;      // int(*)(int)
    
    	static_assert(std::is_same<int, As>::value == true, "");
    	static_assert(std::is_same<int, Bs>::value == true, "");
    	static_assert(std::is_same<int, Cs>::value == true, "");
    	static_assert(std::is_same<int, Ds>::value == true, "");
    	static_assert(std::is_same<int, Es>::value == false, "");
    	static_assert(std::is_same<int, Fs>::value == false, "");
    
    	// std::make_signed/std::make_unsigned: 获取与T对应的带符号/无符号类型,并保留所有cv限定符
    	typedef std::make_signed<int>::type At;                // int
    	typedef std::make_signed<unsigned>::type Bt;           // int
    	typedef std::make_signed<const unsigned>::type Ct;     // const int
    
    	static_assert(std::is_same<int, At>::value == true, "");
    	static_assert(std::is_same<int, Bt>::value == true, "");
    	static_assert(std::is_same<int, Ct>::value == false, "");
    
    	typedef std::make_unsigned<int>::type Au;                // unsigned int
    	static_assert(std::is_same<unsigned, Au>::value == true, "");
    
    	// std::remove_all_extents/std::remove_extent: 移除所有/数组范围
    	typedef std::remove_all_extents<int>::type Av;                // int
    	typedef std::remove_all_extents<int[24]>::type Bv;            // int
    	typedef std::remove_all_extents<int[24][60]>::type Cv;        // int
    	typedef std::remove_all_extents<int[][60]>::type Dv;          // int
    	typedef std::remove_all_extents<const int[10]>::type Ev;      // const int
    
    	static_assert(std::is_same<int, Av>::value == true, "");
    	static_assert(std::is_same<int, Bv>::value == true, "");
    	static_assert(std::is_same<int, Cv>::value == true, "");
    	static_assert(std::is_same<int, Dv>::value == true, "");
    	static_assert(std::is_same<int, Ev>::value == false, "");
    
    	typedef std::remove_extent<int[24][60]>::type Cw;        // int[60]
    	typedef std::remove_extent<int[][60]>::type Dw;          // int[60]
    	static_assert(std::is_same<int, Cw>::value == false, "");
    	static_assert(std::is_same<int, Dw>::value == false, "");
    
    	// std::underlying_type: 获取枚举类型T的基础类型
    	enum class Aa { a, b, c };
    	enum Ba : short { x, y, z };
    	typedef std::underlying_type<Aa>::type A_under;   // int
    	typedef std::underlying_type<Ba>::type B_under;   // short
    
    	static_assert(std::is_same<int, A_under>::value == true, "");
    	static_assert(std::is_same<int, B_under>::value == false, "");
    
    	// 3.Other type generators
    	// std::aligned_storage: 将内存分配与对象创建分离时使用
    	// std::aligned_union: Obtains a POD type suitable for use as storage for any object whose type is listed in Types, and a size of at least Len
    
    	// std::common_type: 在类型列表中获取所有类型都可以转换为的通用类型
    	typedef std::common_type<char, short, int>::type Ab;           // int
    	typedef std::common_type<float, double>::type Bb;              // double
    	typedef std::common_type<R, B>::type Cb;					   // B
    	typedef std::common_type<const int, volatile int>::type Db;    // int
    
    	static_assert(std::is_same<int, Ab>::value == true, "");
    	static_assert(std::is_same<int, Bb>::value == false, "");
    	static_assert(std::is_same<int, Cb>::value == false, "");
    	static_assert(std::is_same<int, Db>::value == true, "");
    
    	// std::conditional: 根据cond是true还是false,获取成员类型
    	typedef std::conditional<true, int, float>::type Ac;                      // int
    	typedef std::conditional<false, int, float>::type Bc;                     // float
    	typedef std::conditional<std::is_integral<Ac>::value, long, int>::type Cc; // long
    	typedef std::conditional<std::is_integral<Bc>::value, long, int>::type Dc; // int
    
    	static_assert(std::is_same<int, Ac>::value == true, "");
    	static_assert(std::is_same<int, Bc>::value == false, "");
    	static_assert(std::is_same<int, Cc>::value == false, "");
    	static_assert(std::is_same<int, Dc>::value == true, "");
    
    	// std::enable_if: 如果条件满足则启用类型
    	// std::result_of: Obtains the result type of a call to Fn with arguments of the types listed in ArgTypes
    
    	return 0;
    }

    以上代码主要参考:cplusplus  cppreference

    GitHubhttps://github.com/fengbingchun/Messy_Test

    展开全文
  • C++的type_traits是一套纯粹编译期的逻辑,可以进行一些类型判断、分支选择等,主要用于模板编程。使用type_traits并不难,但是我们希望能够更加深入了解其实现方式,与此同时,可以更进一步体验C++的模板编程。 本...

    C++的type_traits是一套纯粹编译期的逻辑,可以进行一些类型判断、分支选择等,主要用于模板编程。使用type_traits并不难,但是我们希望能够更加深入了解其实现方式,与此同时,可以更进一步体验C++的模板编程。
    本篇文章旨在引导大家自行实现type_traits的基础代码。
    模板编程不像常规的代码,可以有if-else这些流控制语句,我们需要充分利用模板、模板特例、类型转换等特性来实现编译期的一系列判断和类型转换。

    定义基础常量

    第一步,我们需要定义true和false两个常量,所有的type_traits都基于此。我们的目的就是要用一个模板类型来表示是非,其中的value正好是这两个值。之后我们更高级的判断类型都是继承自这两个类型的其中一个,通过这种方式获取value值就可以获取true和false了。
    如果听这个解释有点晕的话,不要紧,我们直接来看代码。这里需要注意的是,既然type_traits都是编译期行为,因此其成员只能是静态不可变成员(编译期就可以确定的成员)。

    struct true_type {
        static constexpr bool value = true;
    };
    
    struct false_type {
        static constexpr bool value = false;
    };
    

    基础类型判断

    有了基础常量,我们可以先做一些简单的类型判断,比如说判断这个类型是不是void。这里的思路是,针对于所有类型的模板,继承自false_type,而针对于void类型,我们给予一个模板特例,让他继承自true_type。这样一来,只有当类型是void的时候才会推导true,其他的就会推导false。请看例程:

    template <typename>
    struct is_void : false_type {};
    
    template <>
    struct is_void<void> : true_type {};
    

    这里我们可以做一些简单的测试,来判断函数的返回值是否为void:

    void test1();
    int test2();
    
    int main(int argc, const char *argv[]) {
        std::cout << is_void<decltype(test1())>::value << std::endl; // 1
        std::cout << is_void<decltype(test2())>::value << std::endl; // 0
        return 0;
    }
    

    有了判断void的思路基础,不难写出判断其他类型的,比如说判断是否为浮点数,那么只需要对float,double,long double进行特殊处理即可,请看代码:

    template <typename>
    struct is_floating_point : false_type {};
    
    template <>
    struct is_floating_point<float> : true_type {};
    
    template <>
    struct is_floating_point<double> : true_type {};
    
    template <>
    struct is_floating_point<long double> : true_type {};
    

    整型判断相对复杂一点,需要对char,signed char,unsigned char,short,unsigned short,int,unsigned,long,unsigned long,long long,unsigned long long都进行特例编写,方法相同,不再赘述。

    类型处理

    在上一节编写is_floating_point的时候可能会发现这样的问题:

    int main(int argc, const char *argv[]) {
        std::cout << is_floating_point<const double>::value << std::endl; // 0
        std::cout << is_floating_point<double &>::value << std::endl; // 0
        return 0;
    }
    

    但是照理来说,const类型以及引用类型不应该影响他浮点数的本质,当然,我们也可以针对所有的const以及引用情况都编写模板特例,但这样太麻烦了,如果有办法可以去掉const以及引用这些符号,然后再去判断的话,就会减少我们很多工作量。与此同时,这样的类型处理在实际编程时也是很有用的。
    那么,如何去掉const?请看代码:

    template <typename T>
    struct remove_const {
        using type = T;
    };
    
    template <typename T>
    struct remove_const<const T> {
        using type = T;
    };
    

    同样的思路,当T是const类型时,我们变换成const T,然后只取出T,其他类型时直接透传T。
    同理,用这种方法也可以去除引用:

    template <typename T>
    struct remove_reference {
        using type = T;
    };
    
    template <typename T>
    struct remove_reference<T &> {
        using type = T;
    };
    
    
    template <typename T>
    struct remove_reference<T &&> {
        using type = T;
    };
    

    因此,is_floating_point就可以改写成这样:

    // 基础判断降级为helper
    template <typename>
    struct is_floating_point_helper : false_type {};
    template <>
    struct is_floating_point_helper<float> : true_type {};
    template <>
    struct is_floating_point_helper<double> : true_type {};
    template <>
    struct is_floating_point_helper<long double> : true_type {};
    
    // remove_reference和remove_const的声明
    template <typename>
    struct remove_const;
    template <typename>
    struct remove_reference;
    
    // 实际的is_floating_point
    template <typename T>
    struct is_floating_point : is_floating_point_helper<typename remove_const<typename remove_reference<T>::type>::type> {};
    

    类型选择

    我们搞这样一系列的类型封装,最主要的原因是为了在编译器进行逻辑判断。因此,必然要进行一个选择逻辑,也就是当条件成立时,选择某一个类型,不成立时选择另一个类型。这个功能非常好实现,请看代码:

    template <bool judge, typename T1, typename T2>
    struct conditional {
        using type = T1;
    };
    
    template <typename T1, typename T2>
    struct conditional<false, T1, T2> {
        using type = T2;
    };
    

    当第一个参数为true时,type就与T1相同,否则就与T2相同。

    判断是否相同

    我们有时候还需要判断两个类型是否相同,这部分也很好实现,请看代码:

    template <typename, typename>
    struct is_same : false_type {};
    
    template <typename T>
    struct is_same<T, T> : true_type {};
    

    tips

    其实按照这些逻辑,我们几乎可以写出type_traits中的所有功能了。STL中还实现了合取、析取、取反等操作,只是将逻辑判断转为了模板形式,这些用起来更方便,但不是必须的。大家感兴趣可以阅读这部分源码。

    实现is_base_of

    is_base_of用于判断两个类型是否是继承关系,在C++中已经存在了对应的关键字用于判断:

    struct B {};
    struct D : B {};
    struct A {};
    
    int main(int argc, const char *argv[]) {
        std::cout << __is_base_of(B, D) << std::endl; // 1
        std::cout << __is_base_of(B, A) << std::endl; // 0
        return 0;
    }
    

    __is_base_of关键字就可以完成这样的工作,所以我们封装它为模板即可:

    template <typename B, typename D>
    struct is_base_of : conditional<__is_base_of(B, D), true_type, false_type> {};
    

    但除了这种直接使用编译器提供的关键字外,这个功能还有一种其他的实现方法。
    如何判断一个类是否为一个类的父类呢?其实就看指针能否转换(多态)即可。请看代码:

    template <typename B, typename D>
    true_type test_is_base(B *);
    
    template <typename B, typename D>
    false_type test_is_base(void *);
    
    template <typename B, typename D>
    struct is_base_of : decltype(test_is_base<B, D>(static_cast<D *>(nullptr))) {};
    

    如果D是B的子类,那么就会调用第一个函数,从而推断出返回值是true_type,否则调用第二个函数,推断出返回值是false_type。
    不过这样做还必须加一个判断,就是B和D必须都是类才行,而且需要去掉const等因素,详细代码读者可以自行尝试,不再赘述。

    展开全文
  • type_traits 类型萃取

    2020-02-15 14:57:47
    一、 c++ traits traits是c++模板编程中使用的一种技术,主要功能: 把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用traits提取的属性,使得函数对不同的参数...

    一、 c++ traits

    traits是c++模板编程中使用的一种技术,主要功能:

    把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用traits提取的属性,使得函数对不同的参数表现一致。

    traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性:

    • enum
    • typedef
    • template (partial) specialization

    其中:

    • enum用于将在不同类型间变化的标示统一成一个,它在C++中常常被用于在类中替代define,你可以称enum为类中的define;
    • typedef则用于定义你的模板类支持特性的形式,你的模板类必须以某种形式支持某一特性,否则类型萃取器traits将无法正常工作
    • template (partial) specialization被用于提供针对特定类型的正确的或更合适的版本.

    参考 c++ traits

    二、 c++11 中 type_traits

    通过type_traits可以实现在编译期计算、查询、判断、转换和选择,增强了泛型编程的能力,也增强了程序的弹性,使得我们在编译期就能做到优化改进甚至排错,能进一步提高代码质量。

    1. 基本的type_traits

    1.1 简单的type_traits(以定义结构体/类中的常量为例)
    定义一个编译期常量
    template<typename T>
    struct GetLeftSize{
        //使用静态常量
        static const int value = 1;
        //或者使用 enum
        enum{value = 1};
    };
    

    在c++11中直接继承 std::integral_constant即可。

    template<typename T>
    struct GetLeftSize : std::integral_constant < int, 1 >
    {
    };
    int main(){
        cout << GetLeftSize<float>::value << endl;
        return 0;
    }
    

    std::integral_constant的实现:

    // TEMPLATE CLASS integral_constant
    template<class _Ty,_Ty _Val>
    struct integral_constant{   
    	// convenient template for integral constant types
        static const _Ty value = _Val;
        typedef _Ty value_type;
        typedef integral_constant<_Ty, _Val> type;
        operator value_type() const{   
        	// return stored value
            return (value);
       }
    };
    

    这里利用的是static常量为编译器常量的特,定义了value。使用方法:从std::integral_constant派生,无需自己定义static const常量或enum类型,例如

    template<typename T>
    struct GetSize : std::integral_constant<int, 1>
    {
    };
    

    std有两个定义好的std::integral_constant实例,分别定义了编译期的true和false类型,用途很广:

    typedef integral_constant<bool, true> true_type;
    typedef integral_constant<bool, false> false_type;
    

    1.2类型判断的type_traits

    这些类型判断的type_traits从std::integral_constant派生,用来检查模板类型是否为某种类型,通过这些traits可以获取编译期检查的bool值结果。

    template<typename T>
    struct is_integral; //用来检查T是否为bool、char、char16t_t、char32_t、short、long、long long或者这些类型的无符号整数类型。如果T是这些类型中的某一类型,则std::is_integral::value 为true, 否则为false。
    
    //其他的一些类型判断type_traits
    template<typename T>
    struct is_void; //是否为void类型
    
    template<typename T>
    struct is_floating_point; //是否为浮点类型
    
    //is_const, is_function, is_pointer, is_compound....
    std::is_const<int>::value //false
    std::is_const<const int>::value //true
    

    1.3 判断两个类型之间关系的traits
    traits 类型说明
    template< typename T, typename U>
    struct is_same;
    判断两个类型是否相同
    template< typename T, typename U>
    struct is_base_of;
    判断类型T是否是类型U的基类
    template< typename T, typename U>
    struct is_convertible;
    判断类型T能否转换为类型U

    和type_traits的其他使用一样, 通过 is_xxx::value 获得结果(true/false).


    1.4 类型的转换 traits
    traits类型说明
    template< typename T>
    struct remove_const;
    移除const
    template< typename T>
    struct add_const;
    添加const
    template< typename T>
    struct remove_reference;
    移除引用
    template< typename T>
    struct add_lvalue_reference;
    添加左值引用
    template< typename T>
    struct add_rvalue_reference;
    添加右值引用
    template< typename T>
    struct remove_extent;
    移除数组顶层的维度,比如 int [3][3][2] 变为 int [3][2]
    template< typename T>
    struct remove_all_extent;
    移除数组所有的维度,
    比如 int [3][3][2] 变为 int
    template< typename T>
    struct remove_pointer;
    移除指针
    template< typename T>
    struct add_pointer;
    添加指针
    template< typename T>
    struct decay;
    移除cv(const 与 volatile))或者添加指针
    template< typename … T>
    struct common_type;
    获取公共类型

    通过 ::type来访问这些类型。

    std::cout << std::is_same<const int, std::add_const<int>::type>::value << endl; //结果为true
    std::cout << std::is_same<int, std::remove_all_extent<int[2][2][3]>::type>::value<<endl;
     
    //在根据模板参数创建对象时,要注意移除引用:
    template<typename T>
    typename std::remove_reference<T>::type* Create(){
        typedef typename std::remove_reference<T>::type U;
        return new U();
    }
    //因为模板参数可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用移除
     
    //如果给的模板参数是一个带cv描述符的引用类型,要获取它的原始类型,可以使用decay
    template<typename T>
    typename std::decay<T>::type* Create(){
        typedef typename std::decay<T>::type U;
        return new U();
    }
     
    decay还可以获得函数的指针类型,从而将函数指针变量保存起来,以便在后面延迟调用。
    typdef std::decay<int(double)>::type F; //F为一个函数指针类型, int(*)(double)
    template<typename F>
    struct SimpleFunction{
        using FnTyppe = typename std::decay<F>::type;
        SimpleFunction(F& f): m_fn(f){};
        void Run(){
            m_fn();
        }
         
        FnType m_fn;
    };
     
    

    2.根据条件选择的traits

    std::conditional在编译期根据一个判断式选择两个类型中的一个,和条件表达式的语义类似,类似于一个三元表达式:

    template<bool B, class T, class F>
    struct conditional;
    //在std::conditonal模板参数中,如果B为true,则conditional::type为T,否则为F。
    std::conditional<true, int, double>::type //= int
    

    3. 获取可调用对象返回类型的traits

    在类型推导的时候,decltype和auto可以实现模板函数的返回类型.。比如

    //返回类型后置
    template<typename F, typename Arg>
    auto Func(F f, Arg arg)->decltype(f(arg)){
        return f(arg);
    }
     
    

    c++11提供了另一个traits——result_of,用来在编译期获取一个可调用对象的返回类型。

    template<typename F, class... ArgTypes>
    class result_of<F(ArgTypes...)>;
     
     
    int fn(int) {
    	return int();
    };
    
    typedef int(&fn_ref)(int);
    typedef int(*fn_ptr)(int);
    
    struct fn_class{
        int operator()(int i){
            return i;
        }
    };
    int main(){
        typedef std::result_of<decltype(fn)&(int)>::type A;  //int
        typedef std::result_of<fn_ref(int)>::type B;        //int
        typedef std::result_of<fn_ptr(int)>::type C;        //int
        typedef std::result_of<fn_class(int)>::type D;      //int
        return 0;
    }
    
    //需要注意 std::result_of<Fn(ArgTypes)> 要去Fn为一个可调用对象,而函数类型不是一个可调用对象,因此,不能使用
    typedef std::result_of<decltype(fn)(int)>::type A:  //错误
    
    //需要先将fn转换为一个可调用对象类型,比如:
    typedef std::result_of<decltype(fn)&(int)>::type A;
    typedef std::result_of<decltype(fn)*(int)>::type B;
    typedef std::result_of<std::decay<decltype(fn)>::type(int)>::type C;
    

    4.根据条件禁用或启用某种或某些类型traits

    std::enable_if利用**SFINAE(Substitution Failure Is Not An Error)** 实现条件选择重载函数。

    template<bool B, class T = void>   //T为返回类型,常用作函数的返回类型
    struct enable_if;
    
    // 当B为true的时候,返回类型T,否则编译出错。
    template<class T>       //T只有为合法的类型,才能调用该函数,否则编译出错
    typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t){
        return t;
    }
    // std::is_arithmetic<T> 若 T 为算术类型(即整数类型或浮点类型)或其 cv 限定版本,则提供等于 true 的成员常量 value 。对于任何其他类型, value 为 false 。
    auto r = foo(1);    //返回1
    auto r1 = foo(1.2); //返回1.2
    auto r2 = foo("hello"); //编译出错
    

    可以利用这一点来实现相同函数名,但不同类型参数的函数的重载:

    #include <iostream>
    #include <string>
    #include <type_traits>
    
    //对于arithmetic类型的入参返回0,对于非arithmetic类型返回1,通过arithmetic将所有的入参分为两大类进行处理。
    template<class T>
    typename std::enable_if<std::is_arithmetic<T>::value, int>::type foo(T t) {//函数返回类型为int
    	std::cout << t << std::endl;
    	return t;
    }
    
    template<class T>
    typename std::enable_if<!std::is_arithmetic<T>::value, int>::type foo(T t) {//函数返回类型为int
    	std::cout << typeid(T).name() << std::endl;
    	return 1;
    }
    
    int main() {
    	int num = 5;
    	std::string str = "hello";
    	std::cout << foo(num) << std::endl;
    	std::cout << foo(str) << std::endl;
    
    	return 0;
    }
    

    输出结果:
    在这里插入图片描述

    转载自:cnblog-农民伯伯-c++11——type_traits 类型萃取
    展开全文
  • STL中__type_traits

    2019-07-27 03:54:14
    `iterator_traits`是萃取迭代器的特性,而`__type_traits`是萃取型别的特性。萃取的型别如下: - non-trivial default ctor? - non-trivial copy ctor? - non-trivial assignment operator? - non-trivial dto...
  • C++模板元编程Type_traits

    万次阅读 2018-02-28 21:36:09
    type_traits type_traits是C++11提供的模板元基础库。 type_traits可实现在编译期计算、判断、转换、查询等等功能。 type_traits提供了编译期的true和false。 // type_traits中源码 template &...
  • C++类型萃取之type_traitstype_info

    千次阅读 2018-02-11 17:07:48
    通过type_traits可以实现在编译期计算、查询、判断、转换和选择,增强了泛型编程的能力,也增强了我们程序的弹性,让我们能够在编译期就能够优化改进甚至排错,进一步提高代码质量。 头文件 #include 类型...
  • __type_traits详解

    千次阅读 2017-07-28 14:43:44
    在STL中为了提供通用的操作而又不损失效率,我们用到了一种特殊的技巧,叫traits编程技巧。具体的来说,traits就是 通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型...
  • type_traits,变参函数模板
  • //笔记 实现代码 仅摘取一部分 理解...boost/type_traits/add_const.hpp> or #include <boost/type_traits.hpp> // 实现原理 template <class T> struct add_const { typedef T const type; };.
  • 无法打开包括文件: “type_traits

    千次阅读 2020-08-19 22:16:38
    命令行没有完整的C++编译环境,使用VS配置好的命令行工具,如x64 Native Tools Command Prompt for VS 2019进行编译。
  • 3.多次快速点击 qt creator 能打开的时候原本想凑合用,运行程序报错 找不到 "type_traits" 最基本的c标准库报错,然后从新配置了所有的qt, windows sdk 环境变量,问题依旧。用everything找到 type_traits 拷贝到...
  • C++ type_traits实现原理

    2019-04-24 22:23:47
    以 is_void 为例分析,源代码选择vs2017库文件 ...type_traits> int main() { std::cout << std::is_void<void>::value;// 1 std::cout << std::is_void<int>::va...
  • 【转】《STL源码剖析》学习笔记2——神奇的__type_traits 已有 82 次阅读 2009-12-21 11:10 http://blog.csdn.net/lonelywinter340/archive/2008/11/15/3297892.aspx 在STL中为了提供通用的操作而又不损失效率,...
  • 先去磁盘找到对应的type_traits文件,我的在目录*\VC\Tools\MSVC\14.10.25017\include下 2.在VS的工程下右键->属性->配置属性->C++->常规->附加包含目录 下拉框选择编辑打开 来一张结果图 ...
  • 实现STL type_traits 头文件 注意: 本人是原创, 如若发现雷同, 后果自负 参考网站 STL type_traits 头文件 为了更了解模板元编程的原理, 我决定实现type_traits头文件 注意: 有些只能是编译器内部的实现, 无法使用...
  • c++11 之type_traits

    千次阅读 2016-07-25 18:57:21
    1.type_traits-类型萃取  (1)type_traits可以在一定程度上消除 switch-case 或者 if-else语句,降低程序的复杂度 (2)可以在编译期就检查出是否是正确类型 1.1基本的type_traits 定义编译期常量  struct ...
  • C++ traits编程技法之__type_traits

    千次阅读 2016-06-13 13:59:41
    __type_trivial 双底线前缀,表示是SGI STL以外的东西,不在STL标准范围之内。 __type_trivial负责萃取型别(Type)的特性,究竟是什么特性呢?  注:trivial是英文“无意义的、不重要”的意思。  答曰:这个型别...
  • qt5.11能用的配置放到5.12就不能用了,报错error: 'type_traits' file not found ,5.11用的ndk是r11de,5.12的ndk换成新的使用r19就ok了,其他配置跟跟5.11时候一样。 下图是我的配置 ...
  • 'type_traits': No such file or directory “cl”不是内部或外部命令,也不是可运行的程序或批处理文件 问题原因: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat 中有两行检查命令, 检查...
  • traits的编程技巧极度弥补了C++语言的不足 traits含义就是特性,应用Trait模板参数,使得我们的程序既保持灵活性,同时减少类型参数的数量。能够使得我们对函数进行更加细粒度的控制。traits技术依靠显示模版特殊化...
  • 问题: win10 64 位安装qt5.9.9后,选择vs2015工具集,新建工程编译,报错: QtCore\qglobal.h:45: error: C1083: Cannot open include file: 'type_traits': No such file or director 原因:
  • __type_traits

    2017-07-28 14:59:01
    之前我们对iterator_traits有了一些了解,现在我们要了解__type_traits。 Iterator_traits负责萃取迭代器的特性,__type_traits则负责萃取型别的特性。   对于型别的特性,我们关注的点可能在于对该型别是否需要...
  • STL源码分析之__type_traits型别

    千次阅读 2018-12-04 10:20:32
    这一篇准备探讨__type_traits, 为了将我们在空间配置器里面的提过的__true_type和false_type进行解答. 而type_traits型别对我们STL的效率又有什么影响, 有什么好处? __type_traits介绍 前面介绍的Traits技术在STL中...
  • 一、简单的type_traits我理解的type_traits是利用C++模板特性和static、enum特性定义编译器常量,例如//std::integral_constant源码 typelate, T v> struct integral_constant { static const T value = v; ...
  • c++中type_traits

    2015-04-22 10:21:24
    在STL中为了提供通用的操作而又不损失效率,我们用到了一种特殊的技巧,叫 traits编程技巧。具体的来说,traits就是通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型...
  • 用msvc出现错误 浪费时间自己配置 切换编译器只需要一步,项目》选择mingw》就可以换个编译器打开了!!!
  • C1083: Cannot open include file: 'type_traits': No such file or directory ubuntu安装出现 segmentation fault 2.如何解决:网上说有两个编译器,所以我准备切换编辑器,最后成功。 2.1 本身式msvc模式,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,189
精华内容 7,675
关键字:

type_traits