SFINAE
原则:在模板匹配失败后不会直接报编译错误,而是继续匹配其他模板。我们能利用这个特征来确定某个类型是否有一个特定的成员、支持一个特定的操作或是一个类。
SFINAE函数重载
我们利用SFINAE
函数重载来确认某个类型是否拥有默认构造函数。
template<class T> struct IsDefaultConstructibleT { private: template<class U, class = decltype(U())> static char test(void*);
template<class> static long test(...);
public: static constexpr bool value = std::is_same_v<decltype(test<T>(nullptr)), char>; };
struct S { S() = delete; };
int main() {
std::cout << IsDefaultConstructibleT<int>::value << std::endl;; std::cout << IsDefaultConstructibleT<S>::value << std::endl;
return 0; }
1 0
|
实现SFINAE
函数重载的方法是声明2个test
重载函数模板,具有不同的返回类型,通过返回类型来判断调用的是哪个test
方法。
第1个重载函数模板被设计为模板参数存在默认构造函数才能匹配成功。第2个重载函数模板中形参为可变参数,可以进行通用匹配,匹配优先级最低。
SFINAE偏特化
我们使用SFINAE
偏特化来确认某个类型是否拥有默认构造函数。
template<class T, class = std::void_t<>> struct IsDefaultConstructible : public std::false_type {};
template<class T> struct IsDefaultConstructible<T, std::void_t<decltype(T())>> : public std::true_type {};
struct S { S() = delete; };
int main() {
std::cout << IsDefaultConstructible<int>::value << std::endl;; std::cout << IsDefaultConstructible<S>::value << std::endl;
return 0; }
1 0
|
第1个为IsDefaultConstructible
类模板声明和定义,对IsDefaultConstructible
类模板的实例化都能匹配到该模板。第2个是IsDefaultConstructible
类模板的特化版本,当模板参数存在默认构造函数则会优先匹配到该模板。
检测操作符
template<class T, class = std::void_t<>> struct HasPlusOpt : public std::false_type {};
template<class T> struct HasPlusOpt<T, std::void_t<decltype(std::declval<T>() + std::declval<T>())>> : public std::true_type {};
|
检测成员函数
template<class T, class = std::void_t<>> struct HasAddFunc : public std::false_type {};
template<class T> struct HasAddFunc<T, std::void_t<decltype(std::declval<T>().add())>> : public std::true_type {};
|
检测成员变量
template<class T, class = std::void_t<>> struct HasValue : public std::false_type {};
template<class T> struct HasValue<T, std::void_t<decltype(std::declval<T>().value)>> : public std::true_type {};
|
检测静态成员函数
template<class T, class = std::void_t<>> struct HasStaticFunc : public std::false_type {};
template<class T> struct HasStaticFunc<T, std::void_t<decltype(T::add())>> : public std::true_type {};
|
检测成员类型
template<class T, class = std::void_t<>> struct HasType : public std::false_type {};
template<class T> struct HasType<T, std::void_t<typename T::MemType>> : public std::true_type {};
|
利用宏定义实现"任意"类型检测
#define DEFINE_HAS_TYPE(MemType) \ template<class T, class = std::void_t<>> \ struct HasTypeT_##MemType : public std::false_type {}; \ template<class T> \ struct HasTypeT_##MemType<T, std::void_t<typename T::MemType>> : public std::true_type {}; \
DEFINE_HAS_TYPE(value_type);
|
多特征约束组合
利用std::void_t
的性质,可以在一个特征中组合多个约束。
template<class T, class = std::void_t<>> struct HasVariousT : std::false_type {};
template<class T> struct HasVariousT<T, std::void_t<decltype(std::declval<T>().begin()), typename T::value_type>> : public std::true_type {};
|