第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 ;} //函数
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 = ; /*通过result_of获取返回值类型:代码可读性好*/
result_of<Test(int)>::type a = ; //等价于: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 ;
}
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", , "shanghai"},
{"name2", , "beijing" },
{"name3", , "nanjing" },
{"name4", , "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 ;
}
/*输出结果
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
*/
第18课 类型萃取(2)_获取返回值类型的traits的更多相关文章
- Java学习笔记13---如何理解“子类重写父类方法时,返回值若为类类型,则必须与父类返回值类型相同或为其子类”
子类重新实现父类的方法称重写:重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改:仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类:要么就不修改 ...
- 第17课 类型萃取(1)_基本的type_traits
1. type_traits类型萃取 (1)type_traits通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型的不同而异.在程序设计中可以使用这些tra ...
- 类型萃取(type traits)
1. 类型萃取的作用 类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他.例如:在STL ...
- java 反射获取方法返回值类型
//ProceedingJoinPoint pjp //获取方法返回值类型 Object[] args = pjp.getArgs(); Class<?>[] paramsCls = ne ...
- pycahrm使用docstrings来指定变量类型、返回值类型、函数参数类型
py里面不需要显示声明类型,这和java c这些静态语言不同,虽然python这样做少了一些代码和写代码的困难度,但还是非常多的弊端的,运行速度 代码安全, 这些都是语言本身带来的本的弊端,这些没办法 ...
- JAVA是否允许返回值类型不同的重载overload或覆盖override
在看<Thinking in java>的时候,看到子类的方法和父类的方法名字相同,但是返回值类型不同,然后就开始怀疑这属于覆盖吗,到网上找到了答案,分析见接下来的网址: http://g ...
- STL的迭代器和类型萃取
今天就可以把STL库中迭代器的实现,和类型萃取好好整理一下了 迭代器的设计思维是STL的关键所在,在STL的实际运用和泛型思维,迭代器都扮演着十分重要的角色,STL力求把数据容器和算法的概念分开来,于 ...
- 头一回发博客,来分享个有关C++类型萃取的编写技巧
废话不多说,上来贴代码最实在,哈哈! 以下代码量有点多,不过这都是在下一手一手敲出来的,小巧好用,把以下代码复制出来,放到相应的hpp文件即可,VS,GCC下均能编译通过 #include<io ...
- C++的类型萃取技术
应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作.但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变 ...
随机推荐
- vscode 快捷键
ctrl+/ 单行注释:// 若要多行注释://,先选中这些要注释的行,再按ctrl+/ shift+Alt+A 多行注释 撤销刚才的操作:Ctrl+Z 恢复刚才的操作:Ctrl+Shift+Z ...
- test pthread code
#include <iostream> #include <pthread.h> using namespace std; ; void * add(void *); pthr ...
- 浏览器输入URL按回车后都经历了什么?
在浏览器上输入一个URL,按回车后,这个过程发生了什么? 1.首先,浏览器地址栏输入了URL,先解析URL,检测URL地址是否合法 2.浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直 ...
- 使用json改写网站
效果图 json文件 https://raw.githubusercontent.com/sherryloslrs/timetable/master/timetable.json { "Ti ...
- RabbitMQ 的安装----windows环境
一.RabbitMQ在windows下的安装 RabbitMQ 它依赖于Erlang,在window上安装时,需要先安装Erlang. 首先确定你的window电脑是32位还是64位,然后下载对应版本 ...
- ES6 扩展运算符 三个点(...)
它是什么 es6中引入扩展运算符(...),它用于把一个数组转化为用逗号分隔的参数序列,它常用在不定参数个数时的函数调用,数组合并等情形.因为typeScript是es6的超集,所以typeScrip ...
- 2/18 (pycharm 快捷键、循环、join语句)
Alt + Enter 快速修正 Ctrl + / 行注释/取消行注释 Ctrl + Shift + / 块注释 Ctrl + Alt + I 自动缩进 CTRL + D 复制选定的区域或 ...
- 使用pip命令自动生成项目安装依赖清单
Python项目中经常会带requirements.txt文件,里面是项目所依赖的包的列表,也就是依赖关系清单,这个清单也可以使用pip命令自动生成. pip命令: 1 pip freeze > ...
- jsonify
在flask中通过响应,将json数据显示在网页中,并且将Content-Type该为application/json格式 1,第一种方法: from flask import jsonify @ap ...
- ubuntu 16.04 安装 网易云
现在网易云官网上下载对应版本 文件名:netease-cloud-music_1.0.0-2_amd64_ubuntu16.04.deb 进入下载目录: 正常安装会出现错误 解决的办法是换源 换源教程 ...