Item 27: Familiarize yourself with alternatives to overloading on universal references
Item 27: Familiarize yourself with alternatives to overloading on universal references (Effective Modern C++ 读书笔记)
这一章主要解决Item 26的问题。
解决方法,使用信号调度(Use flag dispatch)
先来解决如下例子的问题:
std::multiset<std::string> names;
template<typename T>
void logAndAdd(T&& name)
{ auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}
当参数为int时无法调用正确重载函数。
中间那些过渡的错误例子就不列举了,以免造成错误记忆,直接上解决方案:
避免使用重载综合引用的同时,在接受参数(name)之后,函数体内设置信号调度函数
template<typename T>
void logAndAdd(T&& name)
{ logAndAddImpl(std::forward<T>(name), std::is_integral<typename std::remove_reference<T>::type>());
}template<typename T>
void logAndAddImpl(T&& name, std::false_type)
{ auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}std::string nameFromIdx(int idx);
void logAndAddImpl(int idx, std::true_type)
{ logAndAdd(nameFromIdx(idx));
}
进行一下解释:std::is_integral作为一个信号参数,对推导后的T进行判断,确定是不是int相关类型,是的话就返回 std::true_type,调用第三个完全匹配的函数,不是的话就返回std::false_type,调用第二个综合引用模板函数。值得注意的一点是typename std::remove_reference::type(C++14中std::remove_reference_t)的作用, 从名字能看的出来,去掉T的引用格式,还原原来的格式。因为如果不加这个操作,T的推导格式是 int&而不是int,是不被std::is_integral认可的。
再来解决构造函数例子的问题:
class Person{
public:template<typename T>explicit Person(T&& n):name(std::forward<T>(n)){}explicit Person(int idx):name(nameFromIdx(idx)){}…
private:std::string name;
整形相关无法调用正确的构造函数,copy和move被推导,同样无法调用正确构造函数。上解决方案:
class Person{
public:template<typename T, typename = std::enable_if_t<!std::is_base_of<Person, std::decay_t<T>>::value&&!std::is_integral<std::remove_reference_t<T>>::value>>explicit Person(T&& n):name(std::forward<T>(n)){...}explicit Person(int idx):name(nameFromIdx(idx)){...}...private:std::string name;
};
解释一下,enable_if_t是一个新属性,主要是实现SFINAE技术,即利用编译器的模板匹配失败来实现某些功能。
std::is_base_of<Person, std::decay_t<T>>::value
std::is_base_of来进行判断类型T是否是Person或者Person的派生类对象,而std::decay_t 的作用类似于之前讲到的 std::remove_reference 还原对象引用T& 本身 T, 所以是必要的。同样能还原 const 成普通对象。
其余部分和之前的例子类似,容易理解。
权衡:
对于完美转发,优点是效率提高,例如当使用Person(“Nancy”) 这个字符串将被转发到对象成员name 中,而如果不使用完美转发的话,将生成一个临时变量来储存这个字符串。
但是完美转发也有缺点,某些参数会导致完美转发失败,会在后面的章节中介绍。还有一个问题当传入一个错误类型时,非完美转发的的报错信息更清楚,容易理解。
例子:
Person p(u"Konrad Zuse"); 这个地方使用了 char16_t(c++11
中表示16-bit字符串) 而不是char(平时使用的string), 当使用非完美转发的构造函数时,编译器会发现传入的既非string,亦非int , 会直接报错,简单明了:char16_t[12] 无法转换成int或者std::string。, 如果使用完美转发,编译器在转发过程中不会报错,只有到了用转发的参数构造std::string时才会报错。这里的报错信息非常难理解,而且特别长。
解决方案如下:
class Person {
public:template <typename T, typename = std::enable_if_t<!std::is_base_of<Person, std::decay_t<T>>::value> &&!std::is_integral<std::remove_reference_t<T>>::value>>explicit Person(T&& n): name(std::forward<T>(n)){static_assert(std::is_constructible<std::string, T>::value,"Parameter n can't be used to construct a std::string");...}...
};
方法就是在编译器报错之前使用static_assert进行检查,如果传入类型能构造string 则继续,如果不能,报错通知具体原因。
个人感悟,如非必要,还是尽量从设计上对这种复杂情况进行规避。毕竟虽然解决了,但是方法有点复杂,不宜读。
由于本人水平有限,如有错误,请读者不吝赐教。
Item 27: Familiarize yourself with alternatives to overloading on universal references相关推荐
- Effective Modern C++ Item 27 熟悉依万能引用型别进行重载的替代方案
Item 27 熟悉依万能引用型别进行重载的替代方案 Item 26说过,万能引用和重载在一起总会产生各种各样的问题,无论是独立函数,成员函数,都最好不要和万能引用放一起重载,其中构造函数和万能引用放 ...
- Item 24: Distinguish universal references from rvalue references
为了声明指向某类型T的右值引用,你会写T&&.这会使我们在看到源码种出现"T&&"时,就认为是一个右值引用.但,没那么简单: void f(Widg ...
- 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用
1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些"讲道理"的人员,这些客户尝试把 ...
- Pytorch和CNN图像分类
Pytorch和CNN图像分类 PyTorch是一个基于Torch的Python开源机器学习库,用于自然语言处理等应用程序.它主要由Facebookd的人工智能小组开发,不仅能够实现强大的GPU加速, ...
- 轻松学Pytorch-使用卷积神经网络实现图像分类
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|人工智能与算法学习 大家好,本篇教程的贡献者来自社区投稿作 ...
- echart的关系图高亮_echart中饼状图的高亮显示。
1 2 3 4 5 6 7 8 9 10 #main{ 11 width:50vw;height:60vh;margin-left:2vw 12 } 13 14 15 16 17 18 19 20 / ...
- android 自定义键盘_Android自定义输入车牌号键盘、车牌简称,数字 ,字母键盘...
本文来自阿钟的投稿,全文阅读大约十分钟 为了便于用户快捷的输入车牌号码便需要自定义个车牌键盘,而不是使用系统的键盘输入,上效果图: 横屏效果 图片 竖屏效果 图片 一.首先我们要来分析一下需要做哪些东 ...
- Effective Objective-C [下]
http://esoftmobile.com/2013/08/17/effective-objective-c-2/ Chapter 6: Blocks and Grand Central Dispa ...
- Python自动化开发学习的第十一周----WEB基础(jquery)
jQuery jQuery 库 - 特性 jQuery 是一个 JavaScript 函数库. jQuery 极大地简化了 JavaScript 编程. jQuery 库包含以下特性: HTML 元素 ...
- 数据结构——队列(C语言实现)
直接上代码 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct Node 5 { 6 int data; ...
最新文章
- Datawhale组队学习 Task03:栈与递归(2天)
- 自建通用Makefile 分享
- Google的Java开发规范
- OpenMP 线程化(Threading)基础(1)--并行计算简介
- java使用localstorage_sessionStorage和localStorage的使用
- 如何在原先的jqgrid中填充新的数据_如何提升NX工程师的逆向能力与速度
- 写接口文档及生成mock数据
- 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---4
- 活动丨4场直播华丽丽来袭,快来参与
- 七年级计算机与信息安全教案,计算机与信息安全教案.docx
- 决策树Decision Tree+ID3+C4.5算法实战
- mysql日志文件转存_【转】Mysql日志文件
- ISR4K-IOS XE EPC
- sublime text_Sublime Text Editor赠品报告和获胜者
- 顶级论文创新点怎么找?中国高校首次获CVPR最佳学生论文奖有感
- 如何检测ajax完成且是成功的,检测ajax调用是否成功
- Guava中这些Map的骚操作,让我的代码量减少了50%
- [免费配音软件]配音助手1.0 阿里云配音软件
- LARS Lasso
- Linux之网络配置