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相关推荐

  1. Effective Modern C++ Item 27 熟悉依万能引用型别进行重载的替代方案

    Item 27 熟悉依万能引用型别进行重载的替代方案 Item 26说过,万能引用和重载在一起总会产生各种各样的问题,无论是独立函数,成员函数,都最好不要和万能引用放一起重载,其中构造函数和万能引用放 ...

  2. Item 24: Distinguish universal references from rvalue references

    为了声明指向某类型T的右值引用,你会写T&&.这会使我们在看到源码种出现"T&&"时,就认为是一个右值引用.但,没那么简单: void f(Widg ...

  3. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些"讲道理"的人员,这些客户尝试把 ...

  4. Pytorch和CNN图像分类

    Pytorch和CNN图像分类 PyTorch是一个基于Torch的Python开源机器学习库,用于自然语言处理等应用程序.它主要由Facebookd的人工智能小组开发,不仅能够实现强大的GPU加速, ...

  5. 轻松学Pytorch-使用卷积神经网络实现图像分类

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|人工智能与算法学习 大家好,本篇教程的贡献者来自社区投稿作 ...

  6. 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 / ...

  7. android 自定义键盘_Android自定义输入车牌号键盘、车牌简称,数字 ,字母键盘...

    本文来自阿钟的投稿,全文阅读大约十分钟 为了便于用户快捷的输入车牌号码便需要自定义个车牌键盘,而不是使用系统的键盘输入,上效果图: 横屏效果 图片 竖屏效果 图片 一.首先我们要来分析一下需要做哪些东 ...

  8. Effective Objective-C [下]

    http://esoftmobile.com/2013/08/17/effective-objective-c-2/ Chapter 6: Blocks and Grand Central Dispa ...

  9. Python自动化开发学习的第十一周----WEB基础(jquery)

    jQuery jQuery 库 - 特性 jQuery 是一个 JavaScript 函数库. jQuery 极大地简化了 JavaScript 编程. jQuery 库包含以下特性: HTML 元素 ...

  10. 数据结构——队列(C语言实现)

    直接上代码 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct Node 5 { 6 int data; ...

最新文章

  1. Datawhale组队学习 Task03:栈与递归(2天)
  2. 自建通用Makefile 分享
  3. Google的Java开发规范
  4. OpenMP 线程化(Threading)基础(1)--并行计算简介
  5. java使用localstorage_sessionStorage和localStorage的使用
  6. 如何在原先的jqgrid中填充新的数据_如何提升NX工程师的逆向能力与速度
  7. 写接口文档及生成mock数据
  8. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---4
  9. 活动丨4场直播华丽丽来袭,快来参与
  10. 七年级计算机与信息安全教案,计算机与信息安全教案.docx
  11. 决策树Decision Tree+ID3+C4.5算法实战
  12. mysql日志文件转存_【转】Mysql日志文件
  13. ISR4K-IOS XE EPC
  14. sublime text_Sublime Text Editor赠品报告和获胜者
  15. 顶级论文创新点怎么找?中国高校首次获CVPR最佳学生论文奖有感
  16. 如何检测ajax完成且是成功的,检测ajax调用是否成功
  17. Guava中这些Map的骚操作,让我的代码量减少了50%
  18. [免费配音软件]配音助手1.0 阿里云配音软件
  19. LARS Lasso
  20. Linux之网络配置

热门文章

  1. VS2022 EGE 游戏开发之吃豆人1.0
  2. 《Java入门从笨鸟到菜鸟》读后感(一)
  3. 二手车交易价格预测:建模调参
  4. SQL报错 BIGINT UNSIGNED value is out of range in xxx
  5. 基于vue的黑马前端项目小兔鲜
  6. 小飞鱼 通达OA工作流打印计数程序再升级(图文)
  7. elasticsearch理论、集群、常用命令、插件使用
  8. 电子签约助力政企互联,提升政务数字化管理水平
  9. python使用selenium爬取股票相关信息
  10. 数据对齐(结构体对齐、类成员对齐、动态内存对齐/指针对齐、函数参数对齐、SIMD对齐)