处理 std::enable_if<...中谓词的逻辑"OR">

handle logical "OR" for predicates in std::enable_if<...>

本文关键字:std OR gt if lt 处理 enable 谓词      更新时间:2023-10-16

说,我的方法有这个返回类型定义(模板和方法无关,因此未显示(,这需要处理enable_if谓词的逻辑"OR":

typename std::enable_if<{predicate_1} or {predicate_2}), void>::type>
my_method(...) { ... }

当然,它不起作用,因为一旦谓词替换中的任何一个失败,该实例将被清除。

我想到的第一个快速而肮脏的解决方案是提出一个定义,该定义评估失败的谓词以false,例如,如下所示:

template<bool, typename T = void>
struct failed_2_false: std::false_type {};
template<typename T>
struct failed_2_false<true, T>: std::true_type {};

那么这将起作用:

typename std::enable_if<failed_2_false<{predicate_1}>::value or failed_2_false:<{predicate_2}>::value,
void>::type>
my_method(...) { ... }

所以,我的问题是: - 有什么/是否有标准 (STL( 惯用方法来处理谓词的逻辑"OR"?

假设您正在使用这样的 SFINAE 来检查类型是否具有T::barT::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<...>已经无法替换。

解决方案是使用在替换时不会失败的谓词。基于这个答案,我们可以使用以下检测习惯用来检测类型是否具有某些属性:

// 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_typehas_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";}

活生生的例子@神霹雳

PS:你所说的"快速和肮脏"并不脏。它快速,干净,并且可以完成工作。如果使用计算结果为std::true_typestd::false_type的谓词,则可以使用普通布尔运算符来构造更复杂的谓词。