假设您正在使用这样的 SFINAE 来检查一个类型是否具有 T::bar 或 T::type 并且它们中的任何一个都应该是 int:
template <typename T>
typename std::enable_if<
std::is_same<typename T::bar,int>::value ||
std::is_same<typename T::type,int>::value, void>::type asdf(){ std::cout << "0";}
template <typename T>
typename std::enable_if<
!std::is_same<typename T::bar,int>::value &&
!std::is_same<typename T::type,int>::value, void>::type asdf(){ std::cout << "1";}
这行不通,因为当T 没有T::bar 和T::type 时,std::is_same<...> 已经无法替换。
解决方案是使用在替换时不会失败的谓词。基于this answer,我们可以使用下面的检测习语来检测一个类型是否有一些属性:
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
只是为了说明,检测一个类型是否有T::type:
template <typename T>
using has_type_t = typename T::type;
template <typename T>
using has_type = detect<T, has_type_t>;
让T::type 成为某种类型只是有点棘手:
template <typename X>
struct has_X_type_helper {
template <typename T>
using type = typename std::enable_if_t<std::is_same_v< typename T::type, X>,int>;
};
template <typename T,typename X>
using has_X_type = detect<T,has_X_type_helper<X>::template type>;
类型特征需要一些样板,我们必须为 T::bar 编写相同的代码:
template <typename X>
struct has_X_bar_helper {
template <typename T>
using type = typename std::enable_if_t<std::is_same_v< typename T::bar, X>,int>;
};
template <typename T,typename X>
using has_X_bar = detect<T,has_X_bar_helper<X>::template type>;
请注意,特征 has_X_type 和 has_X_bar 确实使用 SFINAE,但这是一个实现细节。这些特征在替换时不会失败(除非它们的参数已经失败)但它们评估为std::true_type 或std::false_type。现在上面的SFINAE可以用||和&&来实现了:
template <typename T>
typename std::enable_if<
has_X_type<T,int>::value ||
has_X_bar<T,int>::value, void>::type asdf(){ std::cout << "0";}
template <typename T>
typename std::enable_if<
!has_X_type<T,int>::value &&
!has_X_bar<T,int>::value, void>::type asdf(){ std::cout << "1";}
Live example @ godbolt
PS:你所说的“又快又脏”并不脏。它快速,干净,可以完成工作。如果您使用计算结果为 std::true_type 或 std::false_type 的谓词,则可以使用普通布尔运算符来构造更复杂的谓词。