第19课 类型萃取(3)_类型选择的traits

1. std::conditional

(1)原型:template <bool Cond, class T, class F> struct conditional;

//根据条件获取T或F类型
template<bool Cond, class T, class F> //泛化
struct conditional { typedef T type; };

template<class T, class F>  //偏特化
struct conditional<false, T, F> { typedef F type; };

(2)说明:

  ①当cond为true时,conditional::type被定义为T类型

  ②当cond为false时,conditional::type被定义为F类型

【编程实验】std::conditional

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
    typedef std::conditional<true, int, float>::type A;                 //int
    typedef std::conditional<false, int, float>::type B;                //float
    typedef std::conditional<is_integral<A>::value, long, int>::type C; //long
    typedef std::conditional<is_integral<B>::value, long, int>::type D; //int

    //比较两个类型,输出较大的类型
    typedef std::conditional<(sizeof(long long) > sizeof(long double)),
                              long long,
                              long double>::type max_size_t;
    cout << typeid(max_size_t).name() << endl; //long double
    return 0;
}

2. std::enable_if

(1)enable_if的作用:

  ①当某个 condition 成立时,enable_if可以提供某种类型

  ②具备限定模板参数的作用,可以在编译期检查输入的模板参数是否有效。

  ③可以用来控制重载函数是否可用,以实现强大的重载机制。

(2)std::enable_if的原型

  ①原型: template<bool cond, class T = void> struct enable_if;

//enable_if的可能实现
template<bool Cond, typename T = void>
struct enable_if {}; //注意,没有type类型

template<typename T> //偏特化,注意T的默认值为void
struct enable_if<true, T> { typedef T type; };

  ②在 condition 为真的时候,由于偏特化机制,第2个结构体模板明显是一个更好的匹配,所以 std::enable_if<>::type 就是有效的。

  ③当condition 为假的时候,只有第一个结构体模板能够匹配,所以std::enable_if<>::type 是无效的,会被丢弃。同时,编译器会报错:error: no type named ‘type’ in ‘struct std::enable_if<false, bool>。

【编程实验】利用std::enable_if检查模板参数

#include <iostream>
#include <typeinfo>
using namespace std;

//1. 模板参数只能是arithmetic(整型和浮点型)
template<typename T>
typename std::enable_if<is_arithmetic<T>::value, T>::type
foo1(T t)
{
    return t;
}

//2. 限定入参类型:
template<typename T> //注意限制的是foo1的第2个形参,只能是整型
T foo2(T t, typename std::enable_if<std::is_integral<T>::value, int>::type = 0)
{
    return t;
}

//3. 限定模板参数T的类型 (注意限制的是模板的T参数:为intergral类型)、
//   如果T是integral类型,is_integral<T>::value为false,enable_if<false>::type将报错
template<typename T, class = typename std::enable_if<std::is_integral<T>::value>::type>
T foo3(T t)
{
    return t;
}

//4. 类模板特化时,参数的限定
//前向声明,A为类模板
template<class T, class Enable = void> class A; 

template<class T>  //模板特化
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> //对模板参数的限定
{};

int main()
{
    //1.
    auto r1 = foo1(1);      //返回整数1
    auto r2 = foo1(1.2);    //返回浮点数1.2
    //auto r3 = foo1("test"); //error

    //2. 限定入参类型
    foo2(1, 2); //ok
    //foo2(1, ""); //error,第二个参数应为integral类型

    //3.限定模板参数
    foo3(1);
    //foo3(1.2); //error,模板参数的类型应为integral类型z z

    //4.类模板特化时,参数的限定
    A<double> a1;  //ok,先匹配特化模板,所以a的类型为A<double, double>
    //A<double, double> a2; //error, 显式指式两个参数。因此匹配的是第1个模板,但由于这里只是声明
                            //而未定义类(注意class A和class A{}的区别),所以会报A是个未完整类的错误。
    //A<int>  a3; //先匹配特化模板(失败)。再匹配A<int, void>模板,但由于class A只是声明,会与a2一样。
                  //的错误。

    return 0;
}

(3)注意事项

  ①T的默认值为void类型,即enable_if的第2个模板参数不指定时,当cond为真,默认会获取到的类型为void。

  ②当cond为假时,由于std::enable_if<>::type是无效的,因此编译器会报错。

【编程实验】利用std::enable_if根据条件选择重载函数

#include <iostream>
using namespace std;

//利用std::enable_if根据条件选择重载函数

/********************************************************************************************/
//利用std::enable_if来选择重载的模板函数foo
//(注意,两个模板函数仅返回值不同!而模板参数从形式上看虽然相同,但实参推导后T类型是不同的!)

//1. 模板函数的参数相同,返回值不同函数的重载。(注意,实际推导后形参其实是不同的!)
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value>::type  //T为arithmetic类型时,返回值void
foo(T& t) //两个foo函数,模板参数相同。但实际推导后这里是arithmetic类型。
{
    return;
}

template <class T>
typename std::enable_if<std::is_class<T>::value, T>::type&  //T为class时,T&
foo(T& t)
{
    return t;
}

//2. 模板函数的形参相同,返回值相同的函数重载。(注意,实际推导后形参其实是不同的!)
//函数功能:将输入的参数转为string类型
//(对于arithemic类型调用std::to_string(t),对于string类型返回其本身)
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, string>::type  //返回值string
toString(T& t)
{
    return std::to_string(t);
}

template<class T>
typename std::enable_if<std::is_same<T, string>::value, string>::type //返回值
toString(T& t)
{
    return t;
}

class Test{};

/********************************************************************************************/
//3. 可调用对象包装器的实现
//3.1 无返回值的情况:
template<class FT,class...Args>
auto call(FT&& f, Args&&...args)->   //返回值为void
typename std::enable_if<std::is_void<typename std::result_of<FT(Args...)>::type>::value, void>::type
{
    f(std::forward<Args>(args)...);
}

//3.2 有返回值的情况
template<class FT, class...Args>
auto call(FT&& f, Args&&...args)->   //当f有返回值时,则返回f原来的返回类型
typename std::enable_if<!std::is_void<typename std::result_of<FT(Args...)>::type>::value,
                        typename std::result_of<FT(Args...)>::type>::type
{
    return f(std::forward<Args>(args)...);
}

//3.3 万能包装器(统一以上两种情况)
template<typename FT, class...Args>
auto FuncWrap(FT&& func, Args&& ...args)->decltype(func(std::forward<Args>(args)...))
{
    return func(std::forward<Args>(args)...);
}

int func(int a, int b)
{
    cout << "int func(int a, int b):" << a + b <<  endl;
    return a + b;
}

int main()
{
    //1. 选择foo重载函数(返回值不同)
    int x = 1;
    foo(x);  //匹配第1个模板,返回void类型
    Test t;
    foo(t);  //匹配第2个模板,返回Test&

    //2. 选择toString重载函数(返回值相同)
    cout << toString(x) << endl;
    string s("abc");
    cout << toString(s)<< endl;

    //3. 可调用对象包装器
    auto lda = [](){cout << "do anything you want!" << endl;};
    call(lda); //无返回值
    call([](int a){cout << "a = " << a << endl;}, 1);
    call(func, 1, 2); //带返回值

    FuncWrap(lda);  //无返回值
    FuncWrap(func, 1, 2); //带返回值

    return 0;
}
/*输出结果
e:\Study\C++11\19>g++ -std=c++11 test3.cpp
e:\Study\C++11\19>a.exe
1
abc
do anything you want!
a = 1
int func(int a, int b):3
do anything you want!
int func(int a, int b):3
*/
时间: 2024-08-14 09:43:41

第19课 类型萃取(3)_类型选择的traits的相关文章

c++ 类型萃取(模板类型 运用)

//类型萃取 #pragma once #include<iostream> using namespace std; struct __TrueType//定义类 普通类型(基本类型的) { bool Get() { return true; } }; struct __FalseType//定义类 非基本类型 { bool Get() { return false; } }; template <class _Tp>//模板类 (类型萃取) struct TypeTraits 

C++_模板类与类型萃取技术

在声明变量,函数,和大多数其他类型实体的时候,C++要求我们使用指定的类型.然而,有许多代码,除了类型不同之外,其余部分看起来都是相同的,比如,下面这个例子: bool IsEqual (int left, int right) {     return left == right; } bool IsEqual (const string& left , const string& right) {     return left == right; } void test() {   

第17课 类型萃取(1)_基本的type_traits

1. type_traits类型萃取 (1)type_traits通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型的不同而异.在程序设计中可以使用这些traits来判断一个类型的一些特性,引发C++的函数重载机制,实现同一种操作因类型不同而异的效果. (2)type_traits提供了丰富的编译期计算.查询.判断.转换和选择的帮助类. (3)type_traits的类型选择功能,在一定程序上可以消除冗长的switch-cast或if-else的语句.提

头一回发博客,来分享个有关C++类型萃取的编写技巧

废话不多说,上来贴代码最实在,哈哈! 以下代码量有点多,不过这都是在下一手一手敲出来的,小巧好用,把以下代码复制出来,放到相应的hpp文件即可,VS,GCC下均能编译通过 1 #include<iostream> 2 #include "../../traits/traits.hpp" 3 4 5 using namespace std; 6 7 8 int show(char i, int j) 9 { 10 return 1; 11 } 12 13 struct Stu

类型萃取

类型萃取是实现不同类型数据面对同一函数实现不同的操作,它与类封装的区别是:并不用知道所调用的对象是什么类型,类型萃取是编译后知道类型,先实现:而类的封装则是先定义类型,后实现方法. 类型分为基本类型(POD),和自定义类型. 在这里用模板的特化实现其编程思想: 以memcpy为例,当拷贝的是基本类型(POD)时,只用拷贝所传递指针上的数据,如果是string类型,则需要在堆上开辟空间,所传递的指针如果被直接复制,则有可能(vs下的string类型的实现原理是若字符串不长则以数组保存,若字符串过长

通过模板的特化实现 简单的类型萃取 实现memcppy时候对于特殊类型如string类的拷贝。

C++怎样识别一个对象的类型? typeid可以获取到一个类型的名称,但是不能拿来做变量的声明. [POD类型萃取] // // POD: plain old data 平凡类型(无关痛痒的类型)--基本类型 // 指在C++ 中与 C兼容的类型,可以按照 C 的方式处理. //#include<iostream> #include<string> using namespace std; struct __TrueType {  bool Get()  {   return tr

【干货】C++通过模板特化实现类型萃取实例--实现区分基本类型与自定义类型的memcpy

类型萃取是一种常用的编程技巧,其目的是实现不同类型数据面对同一函数实现不同的操作,如STL中cout的实现,它与类封装的区别是,我们并不用知道我们所调用的对象是什么类型,类型萃取是编译器后知道类型,先实现,而类的封装则是先定义类型,后实现方法.在这里我们可以用模板的特化实现其编程思想. 我们以memcpy为例,当我们拷贝的是基本类型时,只用拷贝所传递指针上的数据,如果是string类型呢,我们则需要在堆上开辟空间,所传递的指针如果被直接复制,则有可能(vs下的string类型的实现原理是若字符串

C++ 模板 之 类型萃取 与 容器适配器

类型萃取 在模板这里主要就是对于模板的不同类型的实例化 有不同的方案 这样可以提高效率等 比如 下面的 顺序表 在扩容时的拷贝 对于没有含有指向空间的指针的类 如int 自动使用memcpy() 对于含有指向空间的指针的类 如string 就自动一个一个的赋值 防止浅拷贝导致两个指针指向同一空间 析构两次时出错 类型萃取实现 主要用到了 模板  模板特化 内嵌型别  也可用函数重载 详见Copy() //(1)-------类型萃取 实现顺序表 //----------------------C

STL的迭代器和类型萃取

今天就可以把STL库中迭代器的实现,和类型萃取好好整理一下了 迭代器的设计思维是STL的关键所在,在STL的实际运用和泛型思维,迭代器都扮演着十分重要的角色,STL力求把数据容器和算法的概念分开来,于是就有了STL的两大部分,容器(container)和泛型算法(algorithms),泛型算法有很多参数都是迭代器. 举一个栗子!泛型算法find()的实现! 1 template<class InputIterator, class T> 2 InputIterator find(InputI

SGI-STL简记(三)-构造、类型萃取特性、未初始化解析

stl_construct.h : 提供多种构造器.销毁器模板函数: 构造器construct提供了给定值构造和默认构造方式,通过调用重载函数_Construct实现: 销毁器destroy提供了销毁指针和迭代器的方式,通过调用重载函数_Destroy实现: _Construct:通过“放置”new,构造实现: _Destroy:通过调用模板类型对象析构函数实现,对于迭代器器方式,则通过__destroy(内部通过__destroy_aux)遍历销毁,因内置数据类型不需要销毁, 故内部需要判断是