IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> c++ advanced (6) more about SFINAE -> 正文阅读

[数据结构与算法]c++ advanced (6) more about SFINAE

?不适合新手看

目录

SFINAE in value space expression

classify by assignment operator

classify by static_cast operator

classify by dynamic_cast operator

classify by constructing?operator

classify by no_except constructing?operator

conditional_t

enable_if

exmaples

type-dispatch

?SFINAE


SFINAE in value space expression

之前我们讨论过对于特定模板的某一个模板参数的偏特化的写法。比如根据某一个能否支持自加操作,是否包含特定对象,这些操作来区分特定模板参数函数的不同处理方式。如果我们希望处理多个不同模板参数之间的关系怎么办呢?这个时候就需要用到上上节课讲到的declval了。

头文件,似乎我只包含#include <iostream>也可以

#include <utility>  

classify by assignment operator

template<class T, class U,class Enable>
struct is_assignable_impl :false_type {};

template<class T,class U>
struct is_assignable_impl<T, U, 
	decltype(void(declval<T>() = declval<U>()))> :true_type {};

template<class T,class U>
struct my_is_assignable:is_assignable_impl<T,U,void>{};

注意is_assignable是c++库里面有的,感兴趣的可以去看看源码

?我们可以测试一下:

static_assert(my_is_assignable<int&, long>::value);
static_assert(!my_is_assignable<int&, int*>::value);

注意上面是int&,因为decltype里面的对象是本身,因此需要&,如果不想加就这么写:

template<class T,class U>
struct is_assignable_impl<T, U, void_t<
	decltype(declval<add_lvalue_reference_t<T>>() = declval<U>())>> :true_type {};

注意到上面void写法有所不同,其实除了上面两种定义方式之外还可以用这种方式:

template<class T,class U>
struct is_assignable_impl<T, U, 
	decltype(declval<add_lvalue_reference_t<T>>() = declval<U>(),void())> :true_type {};

classify by static_cast operator

template<class T, class U,class >
struct ISC_impl :false_type {};

template<class T,class U>
struct ISC_impl<T, U,
	decltype(void(
		static_cast<U>(declval<T>())
		))> :true_type {};

template<class T,class U>
struct my_is_static_castable :ISC_impl<T, U, void> {};

测试:

static_assert(my_is_static_castable<int*, void*>::value);
static_assert(!my_is_static_castable<int, int*>::value);

classify by dynamic_cast operator

template<class T,class >
struct IP_impl :false_type {};

template<class T>
struct IP_impl<T,
	decltype(void(
		dynamic_cast<void*>(declval<T>())
		))> :true_type {};

template<class T>
struct my_is_polymorphic :IP_impl<T, void> {};

测试:

class base {};
class dirived {
	virtual void f();
};
static_assert(my_is_polymorphic<dirived*>::value);
static_assert(!my_is_polymorphic<base*>::value);

classify by constructing?operator

template<class T,class,class...>
struct IC_impl :false_type {};

template<class T,class ...U>
struct IC_impl<T,
	decltype(void(
		::new(declval<void*()>) T(declval<U>()...)
		)),U...> :true_type {};

template<class T, class ...U>
struct my_is_constructible :IC_impl<T, void,U...> {};

测试:

class base 
{
public:
	base(int i, string s) {};
};

static_assert(my_is_constructible<base,int,string>::value);
static_assert(!my_is_constructible<base,int*,string*>::value);

classify by no_except constructing?operator

template<class T,class,class...>
struct INTC_impl :false_type {};

template<class T,class ...U>
struct INTC_impl<T,
	decltype(void(
		::new(declval<void*()>) T(declval<U>()...)
		)),U...>:bool_constant<noexcept(
			::new(declval<void*>()) T(declval<U>()...)
			)>  {};

template<class T, class ...U>
struct my_is_nothrow_constructible :INTC_impl<T, void,U...> {};

测试:

class base 
{
public:
	base(int i, string s)noexcept(true) {};
	base(double i, string s)noexcept(false) {};
};

static_assert(my_is_nothrow_constructible<base,int,string>::value);
static_assert(!my_is_nothrow_constructible<base,double,string>::value);
static_assert(!my_is_nothrow_constructible<base, int*, string>::value);

conditional_t

conditional_t就像是一个三元的操作,它用其中两个来构造第三个,然后根据结果返回第一个还是第二个,大概思路如下代码,stl也提供了对应的版本,包括上面的都有。

template<bool B, class T, class F>
struct my_conditional {using type=T};

template<class T, class F>
struct my_conditional<false, T, F> { using type = F; };

template<bool B,class T,class F>
using my_conditional_t = typename my_conditional<B, T, F>::type;

enable_if

template<bool B, class T=void>
struct my_enable_if { using type = T; };

template<class T>
struct my_enable_if<false, T> {};

template<bool B, class T = void>
using my_enable_if_t = typename my_enable_if<B, T>::type;

template<bool B>
using my_bool_if_t = my_enable_if_t<B, bool>;

如你所见我们只需要两个模板参数就可以完成构建,简单的说,如果们指定传入enable_if的第一个参数为true或者false,我们就可以指定类型为T或者ill-formed,测试:

void main() 
{	
	my_enable_if_t<true, int>i = 1;
	my_enable_if_t<false, int>g = 1;//error
	cout << typeid(g).name() << endl;
}

?当然你也可以这样写:

template<bool Cond, class T = void> struct my_enable_if {};
template<class T> struct my_enable_if<true, T> { typedef T type; };

enable_if 可以在任何地方充当一个类型使用,可以有实际意义,也可以新增一个多余的仅用来 enable / unable 的参数。接下来结合例子讲一讲

exmaples

我们知道对于double,或者float类型,我们没办法判断是否相等,因此如果我们希望实现对floating类型判断是否相等的重载,我们应该怎么做呢?

正常人的做法都是写一个template:

template<class T>
bool Equal(T a, T b) 
{
	return a == b;
}
template<>
bool Equal<double>(double a, double b)
{
	return true;
}

?那么问题来了,如果我们用float呢?又会报错,诚然我们再写一个float的偏特化就能解决但是这并不是问题最终的解决方案,因为可能有成千上万种类似于float或者double的类型需要偏特化。

有两种解决方法:

type-dispatch

template<class T>
bool Equals(T l, T r, true_type)
{
	return true;
}

template<class T>
bool Equals(T l, T r, false_type)
{
	return l == r;
}
template<class T>
bool Equals(T l, T r) 
{
	return Equals(l, r, conditional_t<
		is_floating_point_v<T>, true_type, false_type
	>{});
}

?注意,conditional_t返回的只是type,如果需要转化为instance,需要加大括号,表示从type生成instance。

最后根据生成的true or false,我们可以去匹配下面两种不同的函数。

测试:

void main() 
{
	bool flag= Equals(0.2, 0.3);
	cout << flag << endl;
	flag = Equals(1, 2);
	cout << flag << endl;
}

输出:

1

0

?SFINAE

上面的问题可以用enable_if解决:

template<class T>
bool Equals(T l, T r, enable_if_t<is_floating_point_v<T>>* dummy = nullptr)
{
	return true;
}

template<class T>
bool Equals(T l, T r, enable_if_t<!is_floating_point_v<T>>* dummy = nullptr)
{
	return l == r;
}

?当然我们也可以把enable放在返回值上面,效果一样

template<class T>
enable_if_t<is_floating_point_v<T>,bool> Equals(T l, T r)
{
	return true;
}

template<class T>
enable_if_t<!is_floating_point_v<T>,bool> Equals(T l, T r)
{
	return l == r;
}

输出:

1

0

?当然,template里面的空间不仅仅是typename,我们还可以这样用enable_if

template<int a, int b>
typename enable_if_t<a + b == 233, bool> is233()
{
	return true;
}
template<int a, int b>
typename enable_if_t<a + b != 233, bool> is233()
{
	return false;
}
int main()
{
	cout << is233<1, 232>() << endl << is233<114514, 1919>();
	return 0;
}

输出:

1

0

参考:

https://www.youtube.com/watch?v=ybaE9qlhHvw&t=1462sicon-default.png?t=M276https://www.youtube.com/watch?v=ybaE9qlhHvw&t=1462s
https://github.com/CppCon/CppCon2017icon-default.png?t=M276https://github.com/CppCon/CppCon2017

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-11 22:26:29  更:2022-03-11 22:29:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 14:46:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码