Will I step onto an UB [...]
Technically, yes. But practically, it just won't compile for non-enumeration types. When you write:
std::conditional_t<std::is_enum<T>::value, std::underlying_type_t<T>, foo>;
^^^^^^^^^^^^^^^^^^^^^^^^^
That template parameter must be evaluated before the conditional
template can be instantiated. This is equivalent to all function arguments having to be invoked before the body of the function begins. For non-enumerated types, underlying_type<T>
is incomplete (sure it's specified as undefined in the standard but let's be reasonable), so there is no underlying_type_t
. So the instantiation fails.
What you need to do is delay the instantiation in that case:
template <class T> struct tag { using type = T; };
typename std::conditional_t<
std::is_enum<T>::value,
std::underlying_type<T>,
tag<foo>>::type;
Now, our conditional
instead of selecting types is selecting a metafunction! underlying_type<T>::type
will only be instantiated for T
being an enum. We additionally have to wrap foo
to turn it into a metafunction.
This is a common pattern and was a special thing in Boost.MPL called eval_if
, which would look like:
template <bool B, class T, class F>
using eval_if_t = typename std::conditional_t<B, T, F>::type;
Note that we're both using conditional_t
and ::type
.