I'm having a hard time understanding how to stop code from being evaluated with std::conditional_t in the false branch.
#include <type_traits>
using namespace std;
namespace {
template <typename T>
using BaseDifferenceType = decltype (T{} - T{});
}
int main ()
{
using T = int;
static_assert(! is_enum_v<T>);
BaseDifferenceType<T> xxx {};
// PROBLEM IS HERE - though is_enum is false, we still evaluate underlying_type<T> and complain it fails
using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;
return 0;
}
You can try this online at https://www.onlinegdb.com/uxlpSWVXr.
Compiling (with C++17) gives the error:
error: ‘int’ is not an enumeration type
typedef __underlying_type(_Tp) type;
^~~~ main.cpp: In function ‘int main()’: main.cpp:16:87: error: template argument 2 is invalid
using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;
The answer is simple:
You don't.
std::conditional_t always has three fully evaluated arguments:
Something boolean-like, and two types.
If one of them cannot be evaluated if the other is selected, you need to use a custom template and specialize it appropriately:
template <class T, bool = std::is_enum_v<T>>
struct type_finder { using type = std::underlying_type_t<T>; };
template <class T>
struct type_finder<T, false> { using type = BaseDifferenceType<T>; };
template <class T>
using type_finder_t = typename type_finder<T>::type;
An alternative is using if constexpr and automatic return-type-deduction:
template <class T>
auto type_finder_f() {
if constexpr (std::is_enum_v<T>)
return std::underlying_type_t<T>();
else
return BaseDifferenceType<T>();
}
template <class T>
using type_finder_t = decltype(type_finder_f<T>());
A possible alternative pass through a creation of a custom type traits as follows
template <template <typename...> class Cont, typename ... Ts>
struct LazyType
{ using type = Cont<Ts...>; };
and rewrite your std::conditional as follows
using aaa = std::conditional_t<
std::is_enum_v<T>,
LazyType<underlying_type_t, T>,
LazyType<BaseDifferenceType, T>>::type;
// ............................................^^^^^^
Observe I've added a final ::type: it's the "constructor" of the required type, completely avoiding the unwanted type.
Off Topic Unrequested Suggestion: avoid
using namespace std;
It's considered bad practice
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With