1. std::conditional

(1)原型:template <bool Cond, class T, class F> struct conditional;

//根据条件获取T或F类型
template<bool Cond, class T, class F> //泛化
struct conditional { typedef T type; }; template<class T, class F> //偏特化
struct conditional<false, T, F> { typedef F type; };

(2)说明:

  ①当cond为true时,conditional::type被定义为T类型

  ②当cond为false时,conditional::type被定义为F类型

【编程实验】std::conditional

#include <iostream>
#include <typeinfo>
using namespace std; int main()
{
typedef std::conditional<true, int, float>::type A; //int
typedef std::conditional<false, int, float>::type B; //float
typedef std::conditional<is_integral<A>::value, long, int>::type C; //long
typedef std::conditional<is_integral<B>::value, long, int>::type D; //int //比较两个类型,输出较大的类型
typedef std::conditional<(sizeof(long long) > sizeof(long double)),
long long,
long double>::type max_size_t;
cout << typeid(max_size_t).name() << endl; //long double
return ;
}

2. std::enable_if

(1)enable_if的作用:

  ①当某个 condition 成立时,enable_if可以提供某种类型

  ②具备限定模板参数的作用,可以在编译期检查输入的模板参数是否有效。

  ③可以用来控制重载函数是否可用,以实现强大的重载机制。

(2)std::enable_if的原型

  ①原型: template<bool cond, class T = void> struct enable_if;

//enable_if的可能实现
template<bool Cond, typename T = void>
struct enable_if {}; //注意,没有type类型 template<typename T> //偏特化,注意T的默认值为void
struct enable_if<true, T> { typedef T type; };

  ②在 condition 为真的时候,由于偏特化机制,第2个结构体模板明显是一个更好的匹配,所以 std::enable_if<>::type 就是有效的。

  ③当condition 为假的时候,只有第一个结构体模板能够匹配,所以std::enable_if<>::type 是无效的,会被丢弃。同时,编译器会报错:error: no type named ‘type’ in ‘struct std::enable_if<false, bool>。

【编程实验】利用std::enable_if检查模板参数

#include <iostream>
#include <typeinfo>
using namespace std; //1. 模板参数只能是arithmetic(整型和浮点型)
template<typename T>
typename std::enable_if<is_arithmetic<T>::value, T>::type
foo1(T t)
{
return t;
} //2. 限定入参类型:
template<typename T> //注意限制的是foo1的第2个形参,只能是整型
T foo2(T t, typename std::enable_if<std::is_integral<T>::value, int>::type = )
{
return t;
} //3. 限定模板参数T的类型 (注意限制的是模板的T参数:为intergral类型)、
// 如果T是非integral类型,is_integral<T>::value为false,enable_if<false>::type将报错
template<typename T, class = typename std::enable_if<std::is_integral<T>::value>::type>
T foo3(T t)
{
return t;
} //4. 类模板特化时,参数的限定
//前向声明,A为类模板
template<class T, class Enable = void> class A; template<class T> //模板特化
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> //对模板参数的限定
{}; int main()
{
//1.
auto r1 = foo1(); //返回整数1
auto r2 = foo1(1.2); //返回浮点数1.2
//auto r3 = foo1("test"); //error //2. 限定入参类型
foo2(, ); //ok
//foo2(1, ""); //error,第二个参数应为integral类型 //3.限定模板参数
foo3();
//foo3(1.2); //error,模板参数的类型应为integral类型z z //4.类模板特化时,参数的限定
A<double> a1; //ok,先匹配特化模板,所以a的类型为A<double, double>
//A<double, double> a2; //error, 显式指式两个参数。因此匹配的是第1个模板,但由于这里只是声明
//而未定义类(注意class A和class A{}的区别),所以会报A是个未完整类的错误。
//A<int> a3; //先匹配特化模板(失败)。再匹配A<int, void>模板,但由于class A只是声明,会与a2一样。
//的错误。 return ;
}

(3)注意事项

  ①T的默认值为void类型,即enable_if的第2个模板参数不指定时,当cond为真,默认会获取到的类型为void。

  ②当cond为假时,由于std::enable_if<>::type是无效的,因此编译器会报错。

【编程实验】利用std::enable_if根据条件选择重载函数

#include <iostream>
using namespace std; //利用std::enable_if根据条件选择重载函数 /********************************************************************************************/
//利用std::enable_if来选择重载的模板函数foo
//(注意,两个模板函数仅返回值不同!而模板参数从形式上看虽然相同,但实参推导后T类型是不同的!) //1. 模板函数的参数相同,返回值不同函数的重载。(注意,实际推导后形参其实是不同的!)
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value>::type //T为arithmetic类型时,返回值void
foo(T& t) //两个foo函数,模板参数相同。但实际推导后这里是arithmetic类型。
{
return;
} template <class T>
typename std::enable_if<std::is_class<T>::value, T>::type& //T为class时,T&
foo(T& t)
{
return t;
} //2. 模板函数的形参相同,返回值相同的函数重载。(注意,实际推导后形参其实是不同的!)
//函数功能:将输入的参数转为string类型
//(对于arithemic类型调用std::to_string(t),对于string类型返回其本身)
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, string>::type //返回值string
toString(T& t)
{
return std::to_string(t);
} template<class T>
typename std::enable_if<std::is_same<T, string>::value, string>::type //返回值
toString(T& t)
{
return t;
} class Test{}; /********************************************************************************************/
//3. 可调用对象包装器的实现
//3.1 无返回值的情况:
template<class FT,class...Args>
auto call(FT&& f, Args&&...args)-> //返回值为void
typename std::enable_if<std::is_void<typename std::result_of<FT(Args...)>::type>::value, void>::type
{
f(std::forward<Args>(args)...);
} //3.2 有返回值的情况
template<class FT, class...Args>
auto call(FT&& f, Args&&...args)-> //当f有返回值时,则返回f原来的返回类型
typename std::enable_if<!std::is_void<typename std::result_of<FT(Args...)>::type>::value,
typename std::result_of<FT(Args...)>::type>::type
{
return f(std::forward<Args>(args)...);
} //3.3 万能包装器(统一以上两种情况)
template<typename FT, class...Args>
auto FuncWrap(FT&& func, Args&& ...args)->decltype(func(std::forward<Args>(args)...))
{
return func(std::forward<Args>(args)...);
} int func(int a, int b)
{
cout << "int func(int a, int b):" << a + b << endl;
return a + b;
} int main()
{
//1. 选择foo重载函数(返回值不同)
int x = ;
foo(x); //匹配第1个模板,返回void类型
Test t;
foo(t); //匹配第2个模板,返回Test& //2. 选择toString重载函数(返回值相同)
cout << toString(x) << endl;
string s("abc");
cout << toString(s)<< endl; //3. 可调用对象包装器
auto lda = [](){cout << "do anything you want!" << endl;};
call(lda); //无返回值
call([](int a){cout << "a = " << a << endl;}, );
call(func, , ); //带返回值 FuncWrap(lda); //无返回值
FuncWrap(func, , ); //带返回值 return ;
}
/*输出结果
e:\Study\C++11\19>g++ -std=c++11 test3.cpp
e:\Study\C++11\19>a.exe
1
abc
do anything you want!
a = 1
int func(int a, int b):3
do anything you want!
int func(int a, int b):3
*/

第19课 类型萃取(3)_类型选择的traits的更多相关文章

  1. 类型萃取(type traits)

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

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

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

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

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

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

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

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

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

  6. C++类型萃取

    stl中的迭代器和C++中的类型萃取: http://www.itnose.net/detail/6487058.html 赐教!

  7. c++11——type_traits 类型萃取

    一. c++ traits traits是c++模板编程中使用的一种技术,主要功能:     把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用tr ...

  8. 【C++】模板简述(五):类型萃取

    功能 类型萃取,在STL中用到的比较多,用于判断一个变量是否为POD类型. 简述来说可以用来判断出某个变量是内置类型还是自定义类型. 通过类型萃取,萃取到变量类型,对不同变量进行不同处理,可以提升程序 ...

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

    1. 获取可调用对象返回类型 (1)decltype:获取变量或表达式的类型(见第2课) (2)declval及原型 ①原型:template<class T> T&& d ...

随机推荐

  1. apply、call、bind的区别

    apply.call.bind这三种方法一般用来改变this指向. apply()方法接收两个参数,一个是函数运行的作用域this,另一个是参数数组 call()方法接收两个参数,一个是函数运行的作用 ...

  2. linux远程管理常用命令

    目标 关机/重启 shutdown 查看或配置网卡信息 ifconfig ping 远程登录和复制文件 ssh scp 01. 关机/重启 序号 命令 对应英文 作用 01 shutdown 选项 时 ...

  3. 扯淡设计模式2:java,模板模式,

    模板模式: package com.dayuanit.service; public abstract class UserService { public void login(String use ...

  4. effective_java第23条:请不要新代码中使用原生态类型

    从这条开始涉及泛型相关的点. 从JDK5开始Java新增了“泛型”新特性,例如:List<String>,在这之前则只有List不会限定类型. 如今的JDK版本中还是可以写原生类型,但这会 ...

  5. VC++、MFC Sqlite3数据库的使用

    SQLite数据库是一种本地的轻型数据库,在存储一些本地的数据的时候,或者不需要用到Oracle,SQL2008之类的大型数据库的时候,Sqlite的优势就能够得到发挥.程序需要采集数据存储起来,可以 ...

  6. php英语单词大全95

    abstract抽象的 -挨伯丝拽克特 access存取.访问 -挨克色丝 account账户 -厄靠恩特 action动作 -爱克身 activate激活 -爱克特维特 active活动的 -爱克得 ...

  7. IC卡触点释放时序

    IC卡触点释放时序过程如下: 要点: 终端必须通过把RST置为低电平状态来启动释放时序: 在置RST为低电平之后VCC断电之前,终端必须将CLK和IO设定为低电平状态: 在置RST.CLK和IO为低电 ...

  8. RabbitMQ学习之旅(一)

    RabbitMQ学习总结(一) RabbitMQ简介 RabbitMQ是一个消息代理,其接收并转发消息.类似于现实生活中的邮局:你把信件投入邮箱的过程,相当于往队列中添加信息,因为所有邮箱中的信件最终 ...

  9. LINUX文件删除,但磁盘空间未释放

    最近在进行系统压测,由于服务器节点太多,便写了个简单的脚本,在执行过程中发现,日志文件删除后,磁盘空间只释放了一小部分,任有大部分磁盘空间未释放. 使用lsof | grep delete命令,发现已 ...

  10. SQL-记录查询篇-009

    在学习记录查询之前,学习一些关键字的使用: 1.逻辑运算符:and . or . not  .is null select * from   table_name where  id>2 and ...