【问题标题】:How can I get the integral type of a template argument regardless of whether it's an enum or not无论是否为枚举,如何获取模板参数的整数类型
【发布时间】:2026-02-13 01:20:02
【问题描述】:

我收到了错误:

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

我认为它不应该将底层类型解析为底层类型,因为我首先检查 T 是否为枚举。代码如下:

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' 
}

在调用 std::conditional 时,它不是首先检查条件(第一个参数),而是创建第二个和第三个参数的类,而不考虑条件,因此我为什么得到我不能用'int'调用underlying_type的错误?

如何获取 T 模板参数的整数类型,无论是整数还是枚举?

编辑:我的下一个尝试是将 typedef 放在 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?
}

【问题讨论】:

  • std::conditional 是一个模板——模板为任何实例化提供了一个单独的类型。但是为了能够确定实例化的类型,所有参数都需要被评估......
  • 啊,有道理。
  • 可能会感兴趣(尽管不要认为它是重复的):*.com/questions/44550976/how-stdconditional-works
  • 在纸面上,如果您不使用 underlyong_type_t 实用程序(因为它强制实例化),您可以解决它。延迟一步typename conditional_t&lt;isArgEnum, underlying_type&lt;T&gt;, type_identity&lt;T&gt;&gt;::type

标签: c++ templates typedef


【解决方案1】:

不幸的是,std::integral_underlying_type 在 C++20 之前对 SFINAE 不友好。

然后您可以延迟 std::underlying_type&lt;T&gt;::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;
};

注意std::conditional&lt;cond, T1, T2&gt;::type::type 中的双重::type(隐藏在_t 中)。额外的::type 在条件之外而不是在内部完成(因此需要type_identity)。

【讨论】:

    【解决方案2】:

    是的。问题是条件isArgEnumtrue还是false(即T是否为枚举),std::underlying_type_t&lt;T&gt;必须指定为std::conditional的模板参数。

    您可以应用部分专业化,例如

    template <typename T, typename = void>
    struct Foo
    {};
    template <typename T>
    struct Foo<T, std::enable_if_t<std::is_enum_v<T>>>
    {
        using integral_underlying_type = std::underlying_type_t<T>;
    };
    template <typename T>
    struct Foo<T, std::enable_if_t<std::is_integral_v<T>>>
    {
        using integral_underlying_type = T;
    };
    

    【讨论】:

    • 当你写 std::enable_if_t<:is_enum_v>> 作为专业化中的第二个模板参数时,因为你没有提供第二个参数给 std::enable_if_t 我没有'不明白 typedef 是什么,因为它是 T = void。然后看起来你写了“std::enable_if_t<:is_enum_v>, void>”我不明白。
    • @Zebrafish std::enable_if&lt;std::is_enum_v&lt;T&gt;&gt;::type 如果 T 是一个枚举则无效。如果不是,那就是替换失败。但由于它是在即时上下文中,所以它不是一个错误。 SFINAE。因此,当类型确实解析为 void 时,它将匹配基本模板,这使得特化有效。