SFINAE原则:在模板匹配失败后不会直接报编译错误,而是继续匹配其他模板。我们能利用这个特征来确定某个类型是否有一个特定的成员、支持一个特定的操作或是一个类。

SFINAE函数重载

我们利用SFINAE函数重载来确认某个类型是否拥有默认构造函数。

template<class T>
struct IsDefaultConstructibleT {
private:
// test()尝试替换为U传递的T的默认构造函数调用
template<class U, class = decltype(U())>
static char test(void*);

// test()回退
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;
}

// output
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;
}

// output
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 {};