Search code examples
c++templatestypedef

How can I get the integral type of a template argument regardless of whether it's an enum or not


I'm getting the error:

Error   C2154   '_Ty': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type' 

I thought it shouldn't be resolving underlying_type to underlying_type, because I first check whether T is an enum. Here is the code:

template <typename T>
struct Foo
{
    static inline constexpr bool isArgIntegral = std::is_integral<T>::value;
    static inline constexpr bool isArgEnum = std::is_enum_v<T>;

    using integral_underlying_type = std::conditional<isArgEnum, std::underlying_type_t<T>, T>;



};



int main()

    Foo<int> g; // only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type' 
}

So is it the case that in a call to std::conditional, instead of first checking the condition (1st argument), it creates the classes of the 2nd and 3rd arguments regardless of the condition, and hence why I'm getting the error that I can't call underlying_type with an 'int'?

How do I go about getting the integral type of the T template argument, whether it's an integral or an enum?

Edit: My next attempt is to place the typedef in an if constexpr:

if constexpr (std::is_enum_v<T>)
{
    using integral_underlying_type = std::underlying_type_t<T>;
// Now std::underlying_type_t won't be called at all unless T is enum, right?
}

Solution

  • Unfortunately, std::integral_underlying_type is not SFINAE friendly until C++20.

    You can then delay instantiation of std::underlying_type<T>::type:

    template <typename T>
    struct Foo
    {
        using integral_underlying_type =
            typename std::conditional_t<std::is_enum_v<T>,
                                        std::underlying_type<T>,
                                        type_identity<T>>::type;
    };
    

    Notice the double ::type in std::conditional<cond, T1, T2>::type::type (hidden with _t). The extra ::type is done outside the conditional instead of inside (and so the need of type_identity).