C++ std::random和enum class

3

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

假设我想使用 std::random 的内容来从 enum class 中选择一个值。我该如何应对?我们谈论的是一些非常基础的东西,第一个选择可以正常工作,但 Visual Studio因为我使用了裸枚举而责备我(正确地):

enum Direction:uint8_t {
    Left, Right
};

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

// and so on...

我很惊讶enum-base可以用于经典的enum。但是,只需将单词class添加到混合物中就会导致整个过程无法编译。

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,那我将非常高兴。


1
第二个代码片段无法编译,因为你不能隐式地将作用域枚举转换为其基础类型。你必须进行强制转换,所以 std::uniform_int_distribution<uint8_t> direction(static_cast<std::uint8_t>(Direction::Left), static_cast<std::uint8_t>(Direction::Right)); 应该可以工作。 - Staz
1
@Staz -- 正确,也许是回答的基础,但要准备解释为什么分配给Direction对象的返回类型不行。 - Pete Becker
1
@PeteBecker -- 好的,direction(...) 将返回 std::uint8_t。因此,对于作用域枚举,也不会有任何隐式转换。你还需要进行强制类型转换。代码如下:auto rand_dir = static_cast<Direction>(direction(...)); - Staz
4
关于作用域枚举的整个说辞是它们不应该在任何方向上与裸整数类型互换。因此,如果我是你,我会退一步想想你是有原因还是没有原因进行现代化。另外,我认为同时强制执行作用域和强类型在语言级别上有点笨拙。理想情况下,我们本来可以独立实现这两个功能。但那艘船早已驶过了很长时间。 - StoryTeller - Unslander Monica
谢谢大家,真的。一个直接的解决方案和对设计伦理的思考正是这种情况所需要的。=)干杯。 - pdm
1个回答

2

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

作为额外奖励,现在您可以获取“enums”中元素的数量:
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{});
}

1
这正是我从“现代”枚举类型中所期望/想要的。这太棒了,谢谢! - pdm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接