C++ Type Traits if_v (automatic type deduction + ensure same type)

85 views Asked by At

Consider the following code

#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;
}

Since we know at compile time what the types of value1 and value2 are, we should not have to specify it. So we could write

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;

so that we can write a simplified if statement if_v<test, value1, value2> (without the type). Ideally I'd also like to ensure that both input values are of the same type. But I am not sure how to achieve this while using auto.


Basically, are there better ways to define if_v such that we can write if_v<test, value1, value2> without having to specify the type, while also somehow static_asserting type equality?

2

There are 2 answers

2
max66 On BEST ANSWER

I'd also like to ensure that both input values are of the same type. But I am not sure how to achieve this while using auto.

What about using SFINAE ?

I mean

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;

or, maybe, simply

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;
0
cigien On

As is often the case, you can solve this by adding another level of indirection. Make your first version of if_v that takes the type explicitly into an implementation detail:

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;

Now you can implement the version with deduced placeholder types by checking whether the deduced types match, and only calling if_v_impl in that case:

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; 

For simplicity, I'm throwing in the false case, since that's not a constant expression, and is sufficient to halt compilation. You can of course produce a custom diagnostic if you really want, for example by delegating to another function that static_asserts inside the body.

Here's a demo.