【问题标题】:C++ std::conditional_t wont compile with std::is_enum_v and std::underlying_type_tC++ std::conditional_t 不会用 std::is_enum_v 和 std::underlying_type_t 编译
【发布时间】:2021-09-22 13:11:12
【问题描述】:

我正在尝试为可以索引的类型使用类型特征,例如std::vector,它应该包括枚举类型,因为我可以将它们转换为它们的底层类型。

到目前为止,我已经写了以下特征。

#include <iostream>
#include <type_traits>
#include <cinttypes>
#include <vector>
#include <utility>

template<typename T>
struct is_unsigned_integral :
    std::integral_constant<
        bool,
        std::is_integral<T>::value &&
        std::is_unsigned<T>::value
    > {};

template<typename T>
inline constexpr auto is_unsigned_integral_v = 
    is_unsigned_integral<T>::value;

template<typename, typename = void>
struct is_index : std::false_type {};

template<typename T>
struct is_index<
    T, 
    std::enable_if_t<
        is_unsigned_integral_v<
            std::conditional_t<
                std::is_enum_v<T>,
                std::underlying_type_t<T>,
                T
            >
        >
    >
> : std::true_type {};

template<typename T>
inline constexpr auto is_index_v = is_index<T>::value;

enum class idx : unsigned int {};

int main() {
    static_assert(is_index_v<unsigned int>, "");
    static_assert(is_index_v<idx>, "");
    
    return 0;
}

但我收到以下错误消息

type_traits:2009:15: error: 
      only enumeration types have underlying types
      typedef __underlying_type(_Tp) type;

我会期待以下

std::conditional_t<
    std::is_enum_v<T>,
    std::underlying_type_t<T>,
    T
>

将八位计算为T 或基础类型为Tenum

我将如何进行这项工作?

【问题讨论】:

  • 出于好奇,您使用的是什么编译器?使用 gcc,静态断言失败(原因是您的错误消息所说的,但由于 SFINAE,它不应该是错误)godbolt.org/z/cs7Gbjc3n
  • @463035818_is_not_a_number 在replit.com 中使用时使用clang。我目前正在尝试更适应类型特征和元编程:)
  • 还有clang我得到一个不同的错误godbolt.org/z/xn5PfscPG,虽然我实际上不确定因为std::underlying_type&lt;T&gt;是UB直到C++20,当T不是枚举时,保证什么,
  • @463035818_is_not_a_number 我已经在其他使用 gcc / clang 的在线编译器中尝试过你的答案,它似乎在 gcc 下工作正常,但 clang 仍然给出错误
  • clang godbolt.org/z/Tqe8YG3vT没有错误

标签: c++ metaprogramming typetraits


【解决方案1】:

替换失败,因为没有std::underlying_type_t&lt;unsigned int&gt;

你可以单独专攻:

#include <iostream>
#include <type_traits>
#include <cinttypes>
#include <vector>
#include <utility>

template<typename T>
struct is_unsigned_integral :
    std::integral_constant<
        bool,
        std::is_integral<T>::value &&
        std::is_unsigned<T>::value
    > {};

template<typename T>
inline constexpr auto is_unsigned_integral_v = 
    is_unsigned_integral<T>::value;

template<typename, typename = void>
struct is_index : std::false_type {};

template<typename T>
struct is_index<
    T, 
    std::enable_if_t< is_unsigned_integral_v<T> >    
> : std::true_type {};

template <typename T>
struct is_index<T,std::enable_if_t<std::is_enum_v<T> >> : is_index<std::underlying_type_t<T> > {};


template<typename T>
inline constexpr auto is_index_v = is_index<T>::value;

enum class idx : unsigned int {};

int main() {
    static_assert(is_index_v<unsigned int>, "");
    static_assert(is_index_v<idx>, "");
    
    return 0;
}

PS:来自cppreference/std::underlying_type

如果 T 是一个完整的枚举(enum)类型,则提供一个成员 typedef type 来命名 T 的底层类型。

否则,行为未定义。 (直到 C++20)

否则,如果 T 不是枚举类型,则没有成员类型。否则(T 是不完整的枚举类型),程序是非良构的。 (C++20 起)

我不得不承认,我不确定在您的示例中,未定义的行为(在 C++20 之前)如何与 SFINAE 一起使用。不过,请注意,上面不是问题,因为它仅在T 实际上是枚举类型时才使用std::underlying_type_t&lt;T&gt;

【讨论】:

  • 感谢这个非常优雅的解决方案。按预期工作!
猜你喜欢
  • 1970-01-01
  • 2015-03-18
  • 2019-06-06
  • 2014-05-12
  • 2015-09-12
  • 2015-07-23
  • 1970-01-01
  • 2015-05-03
  • 2019-06-25
相关资源
最近更新 更多