代码之家  ›  专栏  ›  技术社区  ›  alfC

如何使“if constexpr”对SFINAE友好?

  •  1
  • alfC  · 技术社区  · 6 月前

    我有一个使用“经典”SFINAE的代码。

    template<class M> auto power(M const& elem) -> decltype(std::norm(elem)) { return std::norm(elem); }
    
    template<class M, class = std::enable_if_t<(M::rank::value >= 1)>>
    auto power(M const& array) {
        return accumulate(begin(array), end(array), 0.0, [](auto const& alpha, auto const& omega) { return alpha + power(omega); });
    }
    

    这是一个递归(跨维度或秩)的“幂”函数,通过计算较低维度的子元素的幂,最终给出元素的平方和。

    现代C++激励使用 if constexpr 以避免使用SFINAE,如下所示。

    template<class M>
    auto power(M const& array) {
        if constexpr(M::rank::value >= 1) {
            return accumulate(begin(array), end(array), 0.0, [](auto const& alpha, auto const& omega) { return alpha + power(omega); });
        } else {
            return std::norm(array);
        }
    }
    

    问题是,中的表达式 if constexpr 首先必须是一个有效的表达式。 元素没有名为的成员类型 rank ,因此存在编译错误。

    有什么诀窍可以使 if constexpr 谓词,如果表达式无效?

    1 回复  |  直到 6 月前
        1
  •  6
  •   HolyBlackCat    6 月前

    C++20及更新版本:

    if constexpr (requires{requires std::bool_constant</*condition*/>::value;})
    

    还有一个不那么冗长的选项:

    if constexpr (requires{requires /*condition*/;})
    

    但后者更糟,因为如果在编译时条件未知,则会导致硬错误,而前者只是返回false。

    使用前者 in the standard library (另请参阅 In a nested requirement, why use requires bool_constant<X>::value; instead of requires X; ? ).

    C++17:

    使用检测习语:

    #include <type_traits>
    
    namespace detail
    {
        template <typename ...P> struct void_type {using type = void;};
    
        template <typename DummyVoid, template <typename...> typename A, typename ...B> struct is_detected : std::false_type {};
        template <template <typename...> typename A, typename ...B> struct is_detected<typename void_type<A<B...>>::type, A, B...> : std::true_type {};
    }
    
    template <template <typename...> typename A, typename ...B>
    inline constexpr bool is_detected = detail::is_detected<void, A, B...>::value;
    

    然后

    template <typename M>
    using CheckRank = std::enable_if_t<M::rank::value >= 1>;
    
    if constexpr (is_detected<CheckRank, M>)