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的更多相关文章

  1. Java学习笔记13---如何理解“子类重写父类方法时,返回值若为类类型,则必须与父类返回值类型相同或为其子类”

    子类重新实现父类的方法称重写:重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改:仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类:要么就不修改 ...

  2. 第17课 类型萃取(1)_基本的type_traits

    1. type_traits类型萃取 (1)type_traits通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型的不同而异.在程序设计中可以使用这些tra ...

  3. 类型萃取(type traits)

    1. 类型萃取的作用 类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他.例如:在STL ...

  4. java 反射获取方法返回值类型

    //ProceedingJoinPoint pjp //获取方法返回值类型 Object[] args = pjp.getArgs(); Class<?>[] paramsCls = ne ...

  5. pycahrm使用docstrings来指定变量类型、返回值类型、函数参数类型

    py里面不需要显示声明类型,这和java c这些静态语言不同,虽然python这样做少了一些代码和写代码的困难度,但还是非常多的弊端的,运行速度 代码安全, 这些都是语言本身带来的本的弊端,这些没办法 ...

  6. JAVA是否允许返回值类型不同的重载overload或覆盖override

    在看<Thinking in java>的时候,看到子类的方法和父类的方法名字相同,但是返回值类型不同,然后就开始怀疑这属于覆盖吗,到网上找到了答案,分析见接下来的网址: http://g ...

  7. STL的迭代器和类型萃取

    今天就可以把STL库中迭代器的实现,和类型萃取好好整理一下了 迭代器的设计思维是STL的关键所在,在STL的实际运用和泛型思维,迭代器都扮演着十分重要的角色,STL力求把数据容器和算法的概念分开来,于 ...

  8. 头一回发博客,来分享个有关C++类型萃取的编写技巧

    废话不多说,上来贴代码最实在,哈哈! 以下代码量有点多,不过这都是在下一手一手敲出来的,小巧好用,把以下代码复制出来,放到相应的hpp文件即可,VS,GCC下均能编译通过 #include<io ...

  9. C++的类型萃取技术

    应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作.但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变 ...

随机推荐

  1. vue-router beforeEach死循环

    vue中页面跳墙处理 页面跳墙中使用 vue-router中的 beforeEach的死循环问题 问题展现 import Router from 'vue-router' const router = ...

  2. 八大排序算法——冒泡排序(动图演示 思路分析 实例代码java 复杂度分析)

    一.动图演示 二.思路分析 1.  相邻两个数两两相比,n[i]跟n[j+1]比,如果n[i]>n[j+1],则将连个数进行交换, 2.  j++, 重复以上步骤,第一趟结束后,最大数就会被确定 ...

  3. 学习笔记-AngularJs(九)

    到目前为止,我们所做的学习案例都是没有加任何动画效果的,对于以往来说,我们经常会去使用一些动画插件或是css框架(如:animate.css)来点缀我们的网页,这样显得生动,高大上,那么接下来我们可以 ...

  4. wishhack 玩法概览

    抢流量玩法 超级店长玩法 https://www.wishhack.com/article/50.html https://www.wishhack.com/article/43.html

  5. Mac上svn报错解决方案

    具体的报错信息为:xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing ...

  6. SpringCloud使用Feign实现服务间通信

    SpringCloud的服务间通信主要有两种办法,一种是使用Spring自带的RestTemplate,另一种是使用Feign,这里主要介绍后者的通信方式. 整个实例一共用到了四个项目,一个Eurek ...

  7. 【转载】Oracle 中count(1) 、count(*) 和count(列名) 函数的区别

    1)count(1)与count(*)比较: 1.如果你的数据表没有主键,那么count(1)比count(*)快2.如果有主键的话,那主键(联合主键)作为count的条件也比count(*)要快3. ...

  8. ﺑﯘﻟﺒﯘﻟﻼﺭ--思恋--IPA--维吾尔语

    很美的维语歌曲, 迪里拜尔将之唱得十分动人心弦.

  9. 用mybatis中的insert方法插入数据,返回值为1,但数据库却没有数据

    刚才在写东西的时候,用mybatis中的 <insert id="add" parameterType="cn.entity.Computer"> ...

  10. 开始Flask项目

    新建Flask项目. 设置调试模式. 理解Flask项目主程序. 使用装饰器,设置路径与函数之间的关系. 使用Flask中render_template,用不同的路径,返回首页.登录员.注册页. 用视 ...