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 */