?不适合新手看
目录
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=1462shttps://www.youtube.com/watch?v=ybaE9qlhHvw&t=1462s https://github.com/CppCon/CppCon2017https://github.com/CppCon/CppCon2017
|