【问题标题】:C++ std::random and enum classC++ std::random 和枚举类
【发布时间】:2021-11-21 13:04:03
【问题描述】:

我有一个关于更现代的 C++“首选”样式的挑剔问题。

假设我想使用std::random 的内容来从enum class 中选择一个值。我怎么能解决这个问题?我们在这里谈论一些非常基本的东西,第一个选择工作得很好,但是 Visual Studio 责骂我(正确地)使用裸 enums

enum Direction:uint8_t {
    Left, Right
};

std::uniform_int_distribution<uint8_t> direction(Direction::Left, Direction::Right);

// and so on...

我很惊讶枚举基础适用于经典的 enum。但是,简单地将单词 class 添加到组合中会导致整个 shebang 无法编译。

enum class Direction:uint8_t {
    Left, Right
};

std::uniform_int_distribution<uint8_t> direction(Direction::Left, Direction::Right);

// Severity Code    Description Project File    Line    Suppression State
// Error    C2338   invalid template argument for uniform_int_distribution:
//    N4659 29.6.1.1 [rand.req.genl]/1e requires one of short, int, long, long long, unsigned short, 
//    unsigned int, unsigned long, or unsigned long long 
//    C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\
//    include\random    1863    

等等。 =)

为了解决这个问题,我当然可以吞下我的骄傲和原则,但我想遵守现代标准。

如果有人想像这样洗牌enum,如果有人有一些聪明的解决方法或其他最佳实践风格的东西,我会很高兴。

【问题讨论】:

  • 第二个 sn-p 无法编译,因为您无法将作用域枚举隐式转换为其基础类型。你必须强制转换它,所以std::uniform_int_distribution&lt;uint8_t&gt; direction(static_cast&lt;std::uint8_t&gt;(Direction::Left), static_cast&lt;std::uint8_t&gt;(Direction::Right)); 应该可以工作。
  • @Staz -- 正确,也许是答案的基础,但要准备好解释为什么不能将分发的返回类型分配给 Direction 对象。
  • @PeteBecker -- 好吧,direction(...) 将返回 std::uint8_t。同样,范围枚举不会有任何隐式转换。你也必须投那个。比如:auto rand_dir = static_cast&lt;Direction&gt;(direction(...));
  • 范围枚举的整个过程并不意味着可以在任何方向上与裸整数类型互换。因此,如果我是你,我会退后一步,想知道你是否在进行现代化改造。顺便说一句,我认为同时执行作用域和强类型在语言层面上有点笨拙。理想情况下,我们将能够彼此独立地完成这两项工作。但是那艘船很久以前就航行了。
  • 老实说,谢谢大家。一个直接的解决方案和对设计伦理的思考正是这种情况所需要的。 =) 干杯。

标签: c++ random visual-c++ enums


【解决方案1】:

enumenum class 都存在相当大的问题:它们不知道自己的大小,无法迭代等。作为替代,您可以将 std::variant 与虚拟类型一起使用:

struct Left {};
struct Right {};
using Direction = std::variant<Left, Right>;

template<typename V, typename E, std::size_t idx = 0>
auto constexpr cast = [] { // written once, works for any "enum"
    if constexpr (std::is_same_v<std::variant_alternative_t<idx, V>, E>) return idx;
    else return cast<V, E, idx + 1>;
}();

std::uniform_int_distribution direction{cast<Direction, Left>, cast<Direction, Right>};

作为奖励,现在您可以获取“枚举”中的元素数量:

static_assert(std::variant_size_v<Direction> == 2);

您甚至可以遍历它们的枚举器:

template<typename F, typename... Es> // written once, works for any "enum"
constexpr void for_each(F f, std::variant<Es...> const&) {
    (..., (void)f(Es{})); // void cast to ignore possible operator,() overloading
}

int main() {
    for_each([](auto enumerator) {
        std::cout << cast<Direction, decltype(enumerator)> << ' ';
    }, Direction{});
}

【讨论】:

  • 这正是我想要/期望的“现代”枚举类型的东西。这很可爱;谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多