std::enable_if 参数中的 SFINAE

SFINAE inside std::enable_if argument

我有不同的 view 类型,每个类型都有一个 std::size_t View::dimension 成员常量和一个 typename View::value_type 成员类型。

下面的编译类型检查应该验证FromTo是否都是视图(使用is_view<>验证),并且From的内容可以赋值至 To。 (相同的维度和可转换的值类型)。

template<typename From, typename To>
struct is_compatible_view : std::integral_constant<bool,
    is_view<From>::value &&
    is_view<To>::value &&
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value
> { };

is_view<T> 对于任何类型 T 总是计算为 std::true_typestd::false_type。问题在于,如果FromTo不是视图类型,那么From::dimension(例如)可能不存在,而is_compatible_view<From, To>会导致编译错误。 在这种情况下,它应该评估为 std::false_type

is_compatible_view用于带std::enable_if的SFINAE,禁用成员函数。例如一个视图 class 可以有一个成员函数

struct View {
    constexpr static std::size_t dimension = ...
    using value_type = ...

    template<typename Other_view>
    std::enable_if_t<is_compatible_view<Other_view, View>> assign_from(const Other_view&);

    void assign_from(const Not_a_view&);
};

Not_a_view 不是视图,并导致 is_compatible_view<Not_a_view, ...> 中的编译错误。调用 view.assign_from(Not_a_view()) 时,SFINAE 不适用,而是在编译器尝试解析第一个 assign_from 函数时发生编译错误。

如何编写 is_compatible_view 才能使其正常工作?在 C++17 中 std::conjunction<...> 允许这样做吗?

一种方法是使用 std::conditional 之类的方法来延迟对你的类型特征的某些部分的评估,直到我们验证你的类型特征的其他部分已经为真。

即:

// this one is only valid if From and To are views
template <class From, class To>
struct is_compatible_view_details : std::integral_constant<bool,
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value
> { };        

// this is the top level one
template<typename From, typename To>
struct is_compatible_view : std::conditional_t<
    is_view<From>::value && is_view<To>::value,
    is_compatible_view_details<From, To>,
    std::false_type>::type
{ };

请注意,我同时使用了 conditional_t::typeis_compatible_view_details 只有在 FromTo 都是视图时才会被实例化。


一种类似的方法是将 std::conjunction 与上面的方法一起使用,由于短路会类似地延迟评估:

template <class From, class To>
struct is_compatible_view : std::conjunction_t<
    is_view<From>,
    is_view<To>,
    is_compatible_view_details<From, To>
    >
{ };

无论哪种方式,您都需要提取详细信息。


第三种方法是使用 enable_if_t 作为专业:

template <class From, class To, class = void>
struct is_compatible_view : std::false_type { };

template <class From, class To>
struct is_compatible_view<From, To, std::enable_if_t<
    is_view<From>::value &&
    is_view<To>::value &&
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value>>
: std::true_type { };

在这里,如果 enable_if_t 中的任何表达式格式不正确,SFINAE 就会介入,我们只使用主模板,即 false_type