如何让if constexpr具备SFINAE兼容性?解决表达式无效编译问题
问题与解决方案
问题背景
我有一段使用经典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); }); }
这段代码实现了一个跨维度/秩递归的power函数,通过计算低维度子元素的power值,最终得到元素的平方和。
现代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:自定义类型特征检测rank::value的存在性
先定义一个类型特征,用来判断目标类型是否包含rank::value成员:
#include <type_traits> // C++17版本实现 template<typename T, typename = void> struct has_rank_value : std::false_type {}; template<typename T> struct has_rank_value<T, std::void_t<decltype(T::rank::value)>> : std::true_type {}; template<typename T> constexpr bool has_rank_value_v = has_rank_value<T>::value; // C++20版本可改用requires优化检测精度 // template<typename T, typename = void> // struct has_rank_value : std::false_type {}; // template<typename T> // struct has_rank_value<T, std::void_t<typename T::rank>> : std::bool_constant<requires { typename T::rank::value; }> {};
之后在if constexpr中先通过该特征做合法性检测,再判断秩的条件:
#include <numeric> #include <complex> template<class M> auto power(M const& array) { if constexpr(has_rank_value_v<M> && M::rank::value >= 1) { return std::accumulate(std::begin(array), std::end(array), 0.0, [](auto const& alpha, auto const& omega) { return alpha + power(omega); }); } else { return std::norm(array); } }
方案2:C++20+直接用requires表达式内联检测
C++20及以后可以无需额外定义类型特征,直接在if constexpr中用requires表达式完成合法性检测:
#include <numeric> #include <complex> template<class M> auto power(M const& array) { if constexpr(requires { M::rank::value; } && (M::rank::value >= 1)) { return std::accumulate(std::begin(array), std::end(array), 0.0, [](auto const& alpha, auto const& omega) { return alpha + power(omega); }); } else { return std::norm(array); } }
requires { M::rank::value; }会先验证M是否存在rank成员类型,且rank包含value成员。只有当该检测通过时,才会继续判断M::rank::value >=1;若检测不通过,整个条件会被视为false,自动进入else分支。
内容的提问来源于stack exchange,提问作者alfC
相关产品推荐
相关产品推荐

