Reputation: 3265
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_assert
ing type equality?
Upvotes: 2
Views: 76
Reputation: 66210
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;
Upvotes: 2
Reputation: 60228
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 throw
ing 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_assert
s inside the body.
Here's a demo.
Upvotes: 1