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. 网络编程—网络基础概览、socket,TCP/UDP协议

    网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...

  2. 小白的python之路10/22 day1

    一.操作系统 操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序.操作系统所处的位置如下图

  3. manjaro配置

    manjaro配置 Table of Contents manjaro配置 系统 一.初次使用 二.安装软件 输入法 emacs samba 三.配置修改 konsole shell颜色 系统 man ...

  4. bootstrap validator 出现Maximum call stack size exceeded

    如果用 c# 里面用的是 taghelper 的控件,有可能造成 Maximum call stack size exceeded bootstrap validator  必须是继承  bootst ...

  5. python笔记9-字符串操作

    1.定义字符串 字符串有下标从0开始,用[]可以来取下标 2.字符串读取 字符串可以使用循环for,每次取一个取出,循环对象的每个元素 3.字符串修改-字符串不可修改 字符串是不可变变量,一旦定义就不 ...

  6. 18-09-21 numpy 的基础学习01

    # 1关于numpy 的学习import numpy as np # 一 如何创建数组****# 1 有规律的一维数据的创建======# 1 range() 和arange() 区别 貌似没有区别l ...

  7. 外网访问SQLServer数据库holer实现

    外网访问内网SQLServer数据库 内网主机上安装了SQLServer数据库,只能在局域网内访问,怎样从公网也能访问本地SQLServer数据库? 本文将介绍使用holer实现的具体步骤. 1. 准 ...

  8. Java学习笔记35(sql补充)

    在上一篇里,写了数据库的增删该查,没有写完,这里补充 CREATE DATABASE Zs_Base; USE Zs_Base; # 创建表 CREATE TABLE PRODUCT( ID INT ...

  9. springBoot的数据库操作

    一:操作数据库起步 1.Spring-Data-Jpa JPA定义了对象持久化的标准. 目前实现了有Hibernate,TopLink 2.pom添加依赖 <dependency> < ...

  10. 条件分支语句(SWICH语句)

    语法 swich(条件表达式){ Case 表达式: 语句……. Break; Case 表达式: 语句……. Break; Case 表达式: 语句……. Break; default: 语句……. ...