zoukankan      html  css  js  c++  java
  • 第18课 类型萃取(2)_获取返回值类型的traits

    1. 获取可调用对象返回类型

    (1)decltype:获取变量或表达式的类型(见第2课)

    (2)declval及原型

      ①原型:template<class T> T&& declval();——函数模板,返回值T&&

    template <class T>
    typename add_rvalue_reference<T>::type declval() noexcept;

      ②decltype的局限性如果模板参数无构造函数,就不能构造出对象,也就无法获取表达式的类型。如后面【编程实验】例子中的Test类由于无构造函数,无法通过Test()产生临时对象,进而也就无法获取仿函数的返回值类型。

      ③而delval将任意类型 T 转换成T&&引用类型,这样便可不必经过构造函数就能使用类的成员函数

      ④注意, declval是个没有函数体的模板函数,所以直接调用declval函数是错误的。它只能用于不求值的语境(如sizeof或decltype)

    (3)result_of及原型

      ①原型:template<class Fn, class… Args> class result_of<Fn(Args…)>

    //result_of的可能实现
    template<class Fn, class... ArgTypes>  
    struct result_of<Fn(ArgTypes...)>  
    {  
      typedef decltype(declval<Fn>()(declval<ArgTypes>()...)) type;  
    }  

      ②注意:

        A.Fn要求是一个可调用对象。如lambda表达式,函数指针、仿函数。

        B.函数类型不是一个可调用对象,在使用result_of前,要先将函数类型转换为可调用对象(如函数指针或函数引用等)

    【编程实验】获取返回值类型

    #include <iostream>
    
    using namespace std;
    
    /*获取可调用对象(如函数)返回的类型*/
    
    /***********************decltype获取返回值类型*****************/
    //1. decltype
    template<typename F, typename Arg>
    auto func(F f, Arg arg)->decltype(f(arg))
    {
        return f(arg);
    }
    
    /***********************declval获取返回值类型*****************/
    //2. delval
    class Test
    {
        Test() = delete; //删除默认的构造函数
    public:
        int operator()(int i) //仿函数
        {
            return i;
        }
    };
    
    /***********************declval获取返回值类型*****************/
    //3.result_of
    int fn(int) {return 0;}     //函数
    typedef int (&fn_ref)(int); //函数引用
    typedef int (*fn_ptr)(int); //函数指针
    struct ftor {int operator()(int i){return i;}}; //仿函数 
    
    int main()
    {
        /*通过declval获取返回值类型:代码可读性较差*/
        //decltype(Test()(0)) i = 4; //error, 由于构造函数被delete,无法产生临时对象
                                     //也就无法获得仿函数的返回值类型!
                                     
        //declval<Test>()是个函数,返回一个对象的引用。由于引用是对象的别名,因此从语义上看,
        //当然可以通过该对象去调用其成员函数operator(int),传入的实参也是一个对象:declval<int>()。
        decltype(declval<Test>()(declval<int>())) i = 4;
        
        /*通过result_of获取返回值类型:代码可读性好*/
        result_of<Test(int)>::type a = 4; //等价于:decltype(std::declval<Test>()(declval<int>()))
        
        //注意result_of原型:result_of<F(ArgTypes...)> ,
        //其中F为可调用对象:如函数指针(引用)、仿函数对象、lambda表达式等。
        //typedef result_of<decltype(fn)(int)>::type A; //error, 函数类型不是可调用对象。
        typedef result_of<decltype(fn)&(int)>::type A;  //int
        typedef result_of<fn_ref(int)>::type B;         //int
        typedef result_of<fn_ptr(int)>::type C;         //int
        typedef result_of<ftor(int)>::type   D;         //int
        
        cout << std::boolalpha;
        cout << is_same<int, A>::value << endl;  //true
        cout << is_same<int, B>::value << endl;  //true
        cout << is_same<int, C>::value << endl;  //true
        cout << is_same<int, D>::value << endl;  //true
            
        return 0;
    }

    2. result_of和decltype的综合应用

    (1)测试数据:vector<Person> v,当中存放一组个人信息,如姓名、年龄和所在城市等。

    (2)程序的目标:根据指定的关键字将vector中的数据分组。如按age分组后,放入multimap中,得

      ①第1组:{20, {"name1", 20, "shanghai"}}和{20,{"name4", 20, "nanjing" }}

      ②第2组:{25,{"name2", 25, "beijing" }}和{25,{"name3", 25, "nanjing" }},

    【编程实验】decltype和result_of的综合应用

    #include <iostream>
    #include <string>
    #include <map>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    /*推断函数返回值类型*/
    
    /*测试数据:
    vector<Person> v = {{"name1", 20, "shanghai"}, {"name2", 25, "beijing" }, 
                        {"name3", 25, "nanjing" },{"name4", 20, "nanjing" }}; 
                        
    程序的目标:将vector中的Person按city或age分组。
    如按age分组后,得
    第1组:{20, {"name1", 20, "shanghai"}}和{20,{"name4", 20, "nanjing" }}
    第2组:{25,{"name2", 25, "beijing" }}和{25,{"name3", 25, "nanjing" }},
    并存放在multimap中。可第1组key为20,值名"name1"和"name4"的两个Person;
    第2组key为25,值名"name2"和"name3"的两个Person;
    */
    struct Person
    {
        string name;
        int age;
        string city;
    };
    
    //将multimap按city和age分组
    //第1版:T为multimap的key类型:int或string等,用于表示age或string。 Fn为可调用对象,用于返回key的类型
    //缺点: 调用GroupBy1时需要手动指定Key类型,如GroupBy1<int>(...);
    template<typename T, typename Fn>
    multimap<T, Person> GroupBy1(const vector<Person>& vt, const Fn& keySelector)
    {
        multimap<T, Person> map; //Key:为T类型(需要手动指定),值为Person
        std::for_each(vt.begin(), vt.end(), [&](const Person& person)
        {
            map.insert(make_pair(keySelector(person), person)); //keySelector获得指定key的值
                                                                //该person的age或city的值。
        });    
        
        return map;
    }
    
    //第2版:Fn为可调用对象,用于返回key的类型
    //优点:比第1版少指定一个模板参数(由decltype(keySelector(*((Person*)nullptr)))
    //      推断)
    //缺点:decltype(keySelector(*((Person*)nullptr)))比较晦涩难懂
    template<typename Fn>
    auto GroupBy2(const vector<Person>& vt, const Fn& keySelector)->
    multimap<decltype(keySelector(*((Person*)nullptr))),Person>
    {
        //推断KeySelector的返回值类型
        typedef decltype(keySelector(*((Person*)nullptr))) key_type;
                
        //multimap中的Key类型由KeySelector返回值推断出来,值为Person            
        multimap<key_type, Person> map; 
        
        std::for_each(vt.begin(), vt.end(), [&](const Person& person)
        {
            map.insert(make_pair(keySelector(person), person));
        });    
        
        return map;
    }
    
    //第3版:Fn为可调用对象,用于返回key的类型:利用result_of获取Fn的返回值
    //优点:代码可读性好
    template<typename Fn>
    multimap<typename result_of<Fn(Person)>::type,Person>
    GroupBy3(const vector<Person>& vt, const Fn& keySelector)
    {
        //推断KeySelector的返回值类型
        typedef typename result_of<Fn(Person)>::type key_type;
                
        //multimap中的Key类型由KeySelector返回值推断出来,值为Person            
        multimap<key_type, Person> map; 
        
        std::for_each(vt.begin(), vt.end(), [&](const Person& person)
        {
            map.insert(make_pair(keySelector(person), person));
        });    
        
        return map;
    }
    
    template<typename Key>
    void printMap(const multimap<Key, Person>& map)
    {
        for(auto it = map.begin(); it != map.end(); it++)
        {
            cout << it->first << " " << it->second.name <<"," << it->second.age << "," << it->second.city << endl;        
        }
    }
    
    int main()
    {
        vector<Person> v = { {"name1", 20, "shanghai"}, 
                             {"name2", 25, "beijing" }, 
                             {"name3", 25, "nanjing" },
                             {"name4", 20, "nanjing" } 
                           }; 
    
        cout << "*************************version 1********************************" << endl;
        //按年龄分组:其中keySelector为[](const Person& person){return person.age;}
        auto m1 = GroupBy1<int>(v, [](const Person& person){return person.age;});
        printMap(m1);
        
        //按city分组:其中keySelector为[](const Person& person){return person.city;}
        auto m2 = GroupBy1<string>(v, [](const Person& person){return person.city;});
        printMap(m2);
        
        cout << "*************************version 2********************************" << endl;
        //按年龄分组:其中keySelector为[](const Person& person){return person.age;}
        auto m3 = GroupBy2(v, [](const Person& person){return person.age;});
        printMap(m3);
        
        //按city分组:其中keySelector为[](const Person& person){return person.city;}
        auto m4 = GroupBy2(v, [](const Person& person){return person.city;});
        printMap(m4);
        
        //按name分组:其中keySelector为[](const Person& person){return person.name;}
        auto m5 = GroupBy2(v, [](const Person& person){return person.name;});
        printMap(m5);
        
        cout << "*************************version 3********************************" << endl;
        //按年龄分组:其中keySelector为[](const Person& person){return person.age;}
        auto m6 = GroupBy3(v, [](const Person& person){return person.age;});
        printMap(m6);
        
        //按city分组:其中keySelector为[](const Person& person){return person.city;}
        auto m7 = GroupBy3(v, [](const Person& person){return person.city;});
        printMap(m7);
        
        //按name分组:其中keySelector为[](const Person& person){return person.name;}
        auto m8 = GroupBy3(v, [](const Person& person){return person.name;});
        printMap(m8);
        
        return 0;
    }
    /*输出结果
    e:StudyC++1118>g++ -std=c++11 test2.cpp
    e:StudyC++1118>a.exe
    *************************version 1********************************
    20 name1,20,shanghai
    20 name4,20,nanjing
    25 name2,25,beijing
    25 name3,25,nanjing
    beijing name2,25,beijing
    nanjing name3,25,nanjing
    nanjing name4,20,nanjing
    shanghai name1,20,shanghai
    *************************version 2********************************
    20 name1,20,shanghai
    20 name4,20,nanjing
    25 name2,25,beijing
    25 name3,25,nanjing
    beijing name2,25,beijing
    nanjing name3,25,nanjing
    nanjing name4,20,nanjing
    shanghai name1,20,shanghai
    name1 name1,20,shanghai
    name2 name2,25,beijing
    name3 name3,25,nanjing
    name4 name4,20,nanjing
    *************************version 3********************************
    20 name1,20,shanghai
    20 name4,20,nanjing
    25 name2,25,beijing
    25 name3,25,nanjing
    beijing name2,25,beijing
    nanjing name3,25,nanjing
    nanjing name4,20,nanjing
    shanghai name1,20,shanghai
    name1 name1,20,shanghai
    name2 name2,25,beijing
    name3 name3,25,nanjing
    name4 name4,20,nanjing
    */
  • 相关阅读:
    pytest临时文件
    djangoclassmeta说明
    python方法
    字符串统计个数2
    python之fixture作用域
    djangomodel在已有model同步添加新的字段
    将 Access 数据库 转换到Sql Server 中
    .net 实现条码
    条码打印异步调用
    日期控件datepicker 只能选指定段日期案例
  • 原文地址:https://www.cnblogs.com/5iedu/p/7774833.html
Copyright © 2011-2022 走看看