您的位置:首页 > 大数据 > 人工智能

第18课 类型萃取(2)_获取返回值类型的traits

2017-11-02 22:10 267 查看
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:\Study\C++11\18>g++ -std=c++11 test2.cpp
e:\Study\C++11\18>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
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐