zoukankan      html  css  js  c++  java
  • 第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:StudyC++1119>g++ -std=c++11 test3.cpp
    e:StudyC++1119>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
    */
  • 相关阅读:
    Python Socket详细介绍
    Python 并发编程-进程
    python UDP-数据报协议
    python TCP协议详解 三次握手四次挥手和11种状态
    3.爬虫 urlib库讲解 总结
    2.爬虫 urlib库讲解 异常处理、URL解析、分析Robots协议
    1.爬虫 urlib库讲解 Handler高级用法
    0.爬虫 urlib库讲解 urlopen()与Request()
    如何在指定文件夹下进入jupyter notebook
    Markdown常用的几种语法
  • 原文地址:https://www.cnblogs.com/5iedu/p/7782291.html
Copyright © 2011-2022 走看看