C++新经典模板与泛型编程:SFINAE特性的信息萃取

用成员函数重载实现is_default_constructible

首先介绍一个C++标准库提供的可变参类模板std::is_default_constructible。这个类模板的主要功能是判断一个类的对象是否能被默认构造(所谓默认构造,就是构造一个类对象时,不需要给该类的构造函数传递任何参数)。例如,有一个类A和一个类B,代码如下。

#include "killCmake.h"

#include<string>
using namespace std;

class A {
};

class B
{
public:
	B(int tmpval) {
	}
};

int main()
{
	A a_obj;

	// E0291: 类 "B" 不存在默认构造函数
	B b_obj;

	// 要构造类B对象,必须给类B的构造函数提供一个参数
	B b_obj(1);

	return 0;
}

现在,可以使用std::is_default_constructible判断类A和类B对象是否能被默认构造(该类没有构造函数或有一个不带参数的构造函数)。在main()主函数中添加代码:

#include "killCmake.h"

#include<string>

using namespace std;


class A {
};

class B
{
public:
	B(int tmpval) {
	}
};

int main()
{

	std::cout << std::is_default_constructible<int>::value << std::endl;
	std::cout << std::is_default_constructible<double>::value << std::endl;
	std::cout << std::is_default_constructible<A>::value << std::endl;
	std::cout << std::is_default_constructible<B>::value << std::endl;

	return 0;
}

在这里插入图片描述

  • 从结果中可以看到,int、double等基本类型(内部类型)对象以及类A对象都是可以默认构造的(结果为1),而类B对象因为其构造函数带一个形参(该形参没有默认值),所以无法默认构造。

  • 在明白了std::is_default_constructible的功能后,现在就来深入了解一下它的实现源码。

  • 如果让你来实现一个与std::is_default_constructible同样的功能,借此看一看如何使用SFINAE特性萃取一些重要信息(这里要萃取的信息是判断某个类是否“没有构造函数或有一个不带参数的构造函数”,满足这个条件的类就能够默认构造)。IsDefConstructible类模板的代码如下。

template<typename T>
class IsDefConstructible
{
private:
	template<typename  = decltype(T())>
	static std::true_type test(void*);

	template<typename = int>
	static std::false_type test(...);

public:
	static constexpr bool value = IsSameType<decltype(test(nullptr)), std::true_type>::value;
};

  • (1)有两个同名的静态成员函数模板test()。注意观察,第1个test()的返回类型是std::true_type,而第2个test()的返回类型是std::false_type。第1个test()的形参是void*,而第2个test()的形参是3个点(…),这个形参读者应该不陌生,是C语言中的省略号形参,代表它可以接受0到任意多个实参。
    尤其要注意第1个和第2个test()的模板参数,都有默认值,第1个test()的模板默认值比较关键(decltype(T())),要重点留意。两个test()都只有声明而没有实现体,因为做类型推断一类事物(一般涉及decltype)的时候往往不需要具体的实现。
  • (2)对于这两个test()静态成员函数(重载函数),调用的时候,编译器会优先选择有具体形参的test()版本,只有该test()版本不匹配时才会选择带省略号形参的test()版本(带省略号的形参具有最低的匹配优先级)。换句话说,优先匹配第1个test()版本,只有第1个test()版本不匹配时,才会去匹配第2个test()版本。
  • (3)最关键的是静态成员变量value的取值,value的最终取值是一个布尔值true(1)或false(0)。如果value最终取值为1,就表示通过模板参数传递给IsDefConstructible的类对象能默认构造,如果value最终取值为0,就表示通过模板参数传递给IsDefConstructible的类对象不能默认构造。
  • 看一看value的最终取值是经过怎样的计算得到的,也就是重点分析下面这行代码:
IsSameType< decltype(test(nullptr)), std::true_type>::value;
  • 上面这行代码用前面讲过的IsSameType<…>::value判断decltype(test(nullptr))和std::true_type这两个类型是否相等,如果相等就返回true,否则返回false。
  • 重点就是代码段decltype(test(nullptr)),这段代码利用decltype判断test()函数的返回类型:如果传递给IsDefConstructible的类型T支持默认构造,那么显然编译器会选择第1个test()并通过decltype推导出返回类型为std::true_type,从而使IsSameType<…>::value返回true;如果传递给IsDefConstructible的类型T不支持默认构造,那么第1个test()就不会成立(因其类型模板参数的默认值不支持类型T的默认构造导致decltype(T())的写法根本就不成立),根据SFINAE特性,编译器会选择第2个test(),然后通过decltype推导出返回类型为std::false_type,从而使IsSameType<…>::value返回false。
  • 以上就是IsDefConstructible类模板的实现细节。现在,可以对main()主函数的代码行进行修改,测试IsDefConstructible类模板能否正常工作。
#include "killCmake.h"

#include<string>

using namespace std;

// 泛化版本
template<typename T1,typename T2>
struct IsSameType
{
	static const bool value = false;
};

// 特化版本
template<typename T1>
struct IsSameType<T1, T1>
{
	static const bool value = true;
};


class A {
};

class B
{
public:
	B(int tmpval) {
	}
};

template<typename T>
class IsDefConstructible
{
private:
	template<typename  = decltype(T())>
	static std::true_type test(void*);

	template<typename = int>
	static std::false_type test(...);

public:
	static constexpr bool value = IsSameType<decltype(test(nullptr)), std::true_type>::value;
};


int main()
{

	std::cout << IsDefConstructible<int>::value << std::endl;
	std::cout << IsDefConstructible<double>::value << std::endl;
	std::cout << IsDefConstructible<A>::value << std::endl;
	std::cout << IsDefConstructible<B>::value << std::endl;

	return 0;
}

在这里插入图片描述

  • 一切正常。这种技术所对应的代码的核心特点就是使用了两个同名但不同返回值的重载成员函数模板。
  • 当然,实现代码不止上面这一种,换一种写法供读者参考,让IsDefConstructible作为辅助类并且改个名字叫作IsDefConstructibleHelper,代码如下。
template<typename T>
class IsDefConstructibleHelper
{
private:
	template<typename = decltype(T())>
	static std::true_type test(void*);

	template<typename = int>
	static std::false_type test(...); 
public:
	using type = decltype(test(nullptr));
};
  • 上面代码的意思比较好理解,使用using定义了别名type,结合以往的IsDefConstructible类模板不难理解:如果类型T支持默认构造,那么type将为std::true_type类型,否则将为std::false_type类型。现在,改写以往的IsDefConstructible类模板,让其继承刚定义的IsDefConstructibleHelper模板中的type(type是一个类型,为std::true_type或std::false_type),代码如下。
template<typename T>
class IsDefConstructible : public IsDefConstructibleHelper<T>::type
{
};
  • 上面的代码并不难理解,分析一下,当类型T支持默认构造时,IsDefConstructible的父类(IsDefConstructibleHelper::type)就相当于std::true_type类型;当类型T不支持默认构造时,IsDefConstructible的父类就相当于std::false_type类型。此时, std::true_type或std::false_type类型中的静态成员变量value的值为true(1)或false(0)就分别代表类型T支持或不支持默认构造。
  • main()主函数中的代码不需要做任何调整,运行程序,结果没有发生变化,一切正常。
#include "killCmake.h"

#include<string>

using namespace std;

// 泛化版本
template<typename T1,typename T2>
struct IsSameType
{
	static const bool value = false;
};

// 特化版本
template<typename T1>
struct IsSameType<T1, T1>
{
	static const bool value = true;
};


class A {
};

class B
{
public:
	B(int tmpval) {
	}
};

template<typename T>
class IsDefConstructibleHelper
{
private:
	template<typename = decltype(T())>
	static std::true_type test(void*);

	template<typename = int>
	static std::false_type test(...); 
public:
	using type = decltype(test(nullptr));
};

template<typename T>
class IsDefConstructible : public IsDefConstructibleHelper<T>::type
{
};

int main()
{

	std::cout << IsDefConstructible<int>::value << std::endl;
	std::cout << IsDefConstructible<double>::value << std::endl;
	std::cout << IsDefConstructible<A>::value << std::endl;
	std::cout << IsDefConstructible<B>::value << std::endl;

	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/235536.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三层交换原理

三层交换机出现的背景 早期的网络中一般使用二层交换机来搭建局域网&#xff0c;而不同局域网之间的网络互通由路由器来完成。那时的网络流量&#xff0c;局域网内部的流量占了绝大部分&#xff0c;而网络间的通信访问量比较少&#xff0c;使用少量路由器已经足够应付了。 但…

六级高频词汇3

目录 单词 参考链接 单词 400. nonsense n. 胡说&#xff0c;冒失的行动 401. nuclear a. 核子的&#xff0c;核能的 402. nucleus n. 核 403. retail n. /v. /ad. 零售 404. retain vt. 保留&#xff0c;保持 405. restrict vt. 限制&#xff0c;约束 406. sponsor n. …

GRE与顺丰圆通快递盒子

1. DNS污染 随想&#xff1a; 在输入一串网址后&#xff0c;会发生如下变化如果你在系统中配置了 Hosts 文件&#xff0c;那么电脑会先查询 Hosts 文件如果 Hosts 里面没有这个别名&#xff0c;就通过域名服务器查询域名服务器回应了&#xff0c;那么你的电脑就可以根据域名服…

使用阿里巴巴同步工具DataX实现Mysql与ElasticSearch数据同步

一、Linux环境要求 二、准备工作 2.1 Linux安装jdk 2.2 linux安装python 2.3 下载DataX&#xff1a; 三、DataX压缩包导入&#xff0c;解压缩 四、编写同步Job 五、执行Job 六、定时更新 6.1 创建定时任务 6.2 提交定时任务 6.3 查看定时任务 七、增量更新思路 一、Linux环境要…

KubeSphere应用【二】Docker安装

一、Docker安装 1.下载Docker安装包 【地址】Index of linux/static/stable/x86_64/ 2.上传至服务器 # 解压文件 tar -xvf docker-20.10.10.tgz# 将docker 目录中的所有文件复制至/usr/bin/目录下 cp docker/* /usr/bin 3.配置docker.service文件 vim /usr/lib/systemd/sy…

WPF仿网易云搭建笔记(2):组件化开发

文章目录 前言专栏和Gitee仓库依赖属性实战&#xff1a;缩小&#xff0c;全屏&#xff0c;关闭按钮依赖属性操作封装主窗口传递this本身给TitleView标题控件主要代码MainWindow.xmalMainWindow.cs依赖属性方法封装TitleView.csTitleViewModelTitleViewModel实现效果 前言 这次…

【JavaWeb学习专栏 | CSS篇】css简单介绍 css常用选择器集锦

个人主页&#xff1a;[兜里有颗棉花糖(https://xiaofeizhu.blog.csdn.net/) 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaWeb学习专栏】【Java系列】 希望本文内容可以帮助到大家&#xff0c;一起加油吧&#xff01;…

安全访问服务边缘(SASE):解决第三方风险的全方位解决方案

随着数字化时代的到来&#xff0c;企业和组织对于网络安全的需求越来越迫切。传统的安全解决方案已经无法满足复杂多变的网络环境&#xff0c;因此新兴的安全访问服务边缘&#xff08;SASE&#xff09;应运而生。本文将介绍SASE的概念和工作原理&#xff0c;并重点阐述它作为第…

第二百回 如何获取App自身的信息

文章目录 1. 概念介绍2. 使用方法2.1 ClipOval2.2 ClipRRect 3. 示例代码 我们在上一章回中介绍了AspectRatio Widget相关的内容&#xff0c;本章回中将介绍剪裁类组件(Clip).闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里说的剪裁类组件主要是指对…

Koa2从零搭建restful API

Koa2从零搭建restful API: 创建项目文件夹并进入 mkdir koa-projectcd koa-project 初始化项目 npm init 安装 Koa npm install koa koa-router --save 编写示例代码&#xff0c;在 app.js 文件中编写以下代码&#xff1a; // koa项目的入口文件 const Koa require("koa…

018 OpenCV 人脸检测

目录 一、环境 二、分类器原理 2.1、概述 2.2、工作原理 三、人脸检测代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、分类器原理 CascadeClassifier是OpenCV&#xff08;开源计算机视觉库&#xff09;中的一个强大的类…

想进阶JAVA高级程序员吗?多线程必学

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

Truffle的基础语法与js测试语法

truffle编译 truffle compiletruffle部署 truffle migratetruffle测试 使用test文件夹下的所有文件测试 truffle test使用单个文件 测试 truffle test 文件所在位置assert断言 assert.equal 是一种常见的断言函数&#xff0c;用于测试两个值是否相等。它接受两个参数&…

代码随想录算法训练营第四十六天 _ 动态规划_背包问题总结。

学习目标&#xff1a; 动态规划五部曲&#xff1a; ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录&#xff01; 本文大多数内容引用自代码随想录 60天训练营打卡计划&#xff01; 学习内容&#xff1a; …

银行插件导致的Outlook客户端无法连接服务器问题

问题现象 最近遇到好些同事出现outlook客户端无法连接服务器的情况&#xff0c;具体现象就是右下角一直显示【正在尝试连接…】或者【需要密码】&#xff0c;点击【需要密码】按钮&#xff0c;输密码的弹窗是一个完全空白的页面。 此时打开word&#xff0c;右上角那里去登录o…

Postman获取token

问题描述 登录接口中带有token参数&#xff0c;其他接口需要带上token才能正确访问&#xff0c;利用接口查询用户信息时手动在headers中更新token信息并不方便。 解决方案 在登录接口中设置一个名为“token”的环境变量&#xff0c;value为登录接口跑通之后responseBody中返回…

科技提升安全,基于YOLOv4开发构建商超扶梯场景下行人安全行为姿态检测识别系统

在商超等人流量较为密集的场景下经常会报道出现一些行人在扶梯上摔倒、受伤等问题&#xff0c;随着AI技术的快速发展与不断普及&#xff0c;越来越多的商超、地铁等场景开始加装专用的安全检测预警系统&#xff0c;核心工作原理即使AI模型与摄像头图像视频流的实时计算&#xf…

⭐Unity 搭建UDP客户端(01) 配合网络调试助手测试

1.接收来自服务器的消息 using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine;public class UDPManager:MonoBehaviour {public string recvStr; //服务器返回值public string UDPClientAddRess "192.168.2.39&q…

【算法系列篇】递归、搜索和回溯(二)

文章目录 前言1. 两两交换链表中的节点1.1 题目要求1.2 做题思路1.3 代码实现 2. Pow(X,N)2.1 题目要求2.2 做题思路2.3 代码实现 3. 计算布尔二叉树的值3.1 题目要求3.2 做题思路3.3 代码实现 4. 求根节点到叶结点数字之和4.1 题目要求4.2 做题思路4.3 代码实现 前言 前面为大…

Uniapp - 环境搭建 vscode开发

uni-app 基础 创建 uni-app 项目方式 uni-app 支持两种方式创建项目&#xff1a; 通过 HBuilderX 创建&#xff08;需安装 HBuilderX 编辑器&#xff09; 通过命令行创建&#xff08;需安装 NodeJS 环境&#xff09; HBuilderX 创建 uni-app 项目 创建步骤 1.下载安装 H…