2

考虑以下代码

#include <type_traits>

template<bool Test, class T, T val1, T val2>
constexpr T if_v = std::conditional_t<Test, 
                                      std::integral_constant<T, val1>, 
                                      std::integral_constant<T, val2>>::value;

int main()
{
    constexpr size_t value1 = 123;
    constexpr size_t value2 = 456;
    constexpr bool test     = (3 > 2);

    constexpr size_t r0 = if_v<test, size_t, value1, value2>;  // = 123

    return 0;
}

由于我们在编译时就知道value1and的类型是什么value2,所以我们不必指定它。所以我们可以写

template<bool Test, auto val1, auto val2>
constexpr decltype(val1) if_v = std::conditional_t<Test, 
                                                   std::integral_constant<decltype(val1), val1>, 
                                                   std::integral_constant<decltype(val2), val2>>::value;

这样我们就可以编写一个简化的if语句if_v<test, value1, value2>(没有类型)。理想情况下,我还想确保两个输入值的类型相同。但我不确定如何在使用auto.


基本上,有没有更好的方法来定义if_v这样我们可以if_v<test, value1, value2>在不必指定类型的情况下编写,同时还能以某种方式static_assert实现类型相等?

4

2 回答 2

2

我还想确保两个输入值的类型相同。但我不确定如何在使用 auto 时实现这一点。

使用 SFINAE 怎么样?

我是说

template <bool Test, auto v1, auto v2,
          std::enable_if_t<std::is_same_v<decltype(v1), decltype(v2)>, int> = 0>
constexpr auto if_v = std::conditional_t<Test, 
                           std::integral_constant<decltype(v1), v1>, 
                           std::integral_constant<decltype(v2), v2>>::value;

或者,也许,只是

template <bool Test, auto v1, auto v2,
          std::enable_if_t<std::is_same_v<decltype(v1), decltype(v2)>, int> = 0>
constexpr auto if_v = Test ? v1 : v2;
于 2020-11-22T15:04:14.003 回答
1

通常情况下,您可以通过添加另一个间接级别来解决此问题。使您的第一个版本if_v将类型显式地纳入实现细节:

template<bool Test, class T, T val1, T val2>
constexpr T if_v_impl = std::conditional_t<Test,
                            std::integral_constant<T, val1>, 
                            std::integral_constant<T, val2>>::value;

现在,您可以通过检查推导的类型是否匹配来实现具有推导占位符类型的版本,并且仅if_v_impl在这种情况下调用:

template<bool Test, auto val1, auto val2>
constexpr decltype(val1) if_v = std::is_same_v<decltype(val1), decltype(val2)> 
                                ? if_v_impl<Test, decltype(val1), val1, val2> 
                                : throw; 

为简单起见,我throw在错误的情况下使用,因为这不是一个常量表达式,并且足以停止编译。如果您真的需要,您当然可以生成自定义诊断,例如通过委托给static_assert身体内部的另一个函数。

这是一个演示

于 2020-11-22T14:44:41.767 回答