【问题标题】:C++ enum class std::size_t implicit conversionC++ 枚举类 std::size_t 隐式转换
【发布时间】:2017-08-11 10:20:51
【问题描述】:

我通过创建枚举类定义了一个元组及其索引:

/** parameter { key ; value1 ; value1 ; } */
using Parameter = std::tuple<unsigned, unsigned, unsigned>;
enum class ParameterKey : std::size_t {
    KEY = 0,
    VALUE1 = 1,
    VALUE2 = 2
};

现在我想从这个元组中获取一个值:

const auto& key = std::get<ParameterKey::KEY>(*parameterPointer);

我认为intstd::size_t 的隐式转换是由: std::size_t 语法保证的:

enum class ParameterKey : std::size_t {
    ....
}

但我收到此错误

error: no matching function for call to ‘get<KEY>(std::tuple<unsigned int, unsigned int, unsigned int>&)’

这很好用,但是太啰嗦了:

const auto& key = std::get<static_cast<unsigned>(ParameterKey::KEY)>(*parameterPointer);

【问题讨论】:

  • 没有从枚举类到任何东西的隐式转换,您可以创建自己的get
  • 好的,但是: std::size_t 语法的目的是什么?
  • @sukovanej 指定underlying_type

标签: c++ tuples std enum-class


【解决方案1】:

这里没有隐式转换。来自enum

范围内的值没有隐式转换 枚举器转为整数类型,尽管 static_cast 可用于 获取枚举器的数值。

所以,你必须使用static_cast


有一些基于static_cast 的解决方法。例如,可以使用std::underlying_type

template<typename T>
constexpr auto get_idx(T value)
{
    return static_cast<std::underlying_type_t<T>>(value);
}

然后:

const auto& key = std::get<get_idx(ParameterKey::KEY)>(*parameterPointer);

【讨论】:

  • 您需要将constexpr 添加到get_idx 才能完成这项工作。
  • @Holt 是的,没错!
【解决方案2】:

enum class 的全部目的是不能隐式转换为int,因此没有隐式转换。

您可以创建自己的get 版本:

template <ParameterKey key, typename Tuple>
decltype(auto) get(Tuple &&tuple) {
    return std::get<static_cast<std::underlying_type_t<ParameterKey>>(key)>(tuple);
}

然后:

const auto& key = get<ParameterKey::KEY>(*parameterPointer);

【讨论】:

  • 这不是全部目的。它还修复了 enum 的不正确范围,并允许您显式指定基础类型。所以肯定有理由想要enum class 并且还想要隐式转换。
  • @Timmmm 您可以为标准枚举指定底层类型。同意标准枚举的范围,但enum class 的目的不是解决这个问题,它只是引入新功能以及对现有功能已有 20 年反馈的副作用。 IMO,除非您正在处理非常低级的代码,否则您不希望为您的 enum class 进行隐式转换。
  • @Holt,谢谢,我不知何故没有注意到可以为标准枚举指定基础类型。
【解决方案3】:

您可以通过创建一个接受此特定枚举作为参数的数组/向量的特化来进行隐式转换:

template <typename ElementType, typename EnumType>
class enumerated_array: array<ElementType, static_cast<size_t>(EnumType::size_)>
{
    using ParentType = array<ElementType, static_cast<size_t>(EnumType::size_)>;
public:
    ElementType& operator[](EnumType enumerator)
    {
        return ParentType::operator[](static_cast<size_t>(enumerator));
    }
    const ElementType& operator[](EnumType enumerator) const
    {
        return ParentType::operator[](static_cast<size_t>(enumerator));
    }
};

// --------------------------------
// and that's how you use it:

enum class PixelColor: size_t { Red, Green, Blue, size_ };
enumerated_array<uint8_t, PixelColor> pixel;
// Can't use any other enum class as an index
pixel[PixelColor::Green] = 255;

此外,虽然这不是这个问题的主题,但这种方法与枚举迭代器的协同作用非常好:

template <typename T>
class EnumRangeType
{
public:
    class Iterator
    {
    public:
        Iterator(size_t value):
            value_(value)
        { }

        T operator*() const
        {
            return static_cast<T>(value_);
        }

        void operator++()
        {
            ++value_;
        }

        bool operator!=(Iterator another) const
        {
            return value_ != another.value_;
        }

    private:
        size_t value_;
    };

    static Iterator begin()
    {
        return Iterator(0);
    }

    static Iterator end()
    {
        return Iterator(static_cast<size_t>(T::size_));
    }
};
template <typename T> constexpr EnumRangeType<T> enum_range;

// --------------------------------
// and that's how you use it:

void make_monochrome(enumerated_array<uint8_t, PixelColor>& pixel)
{
    unsigned int total_brightness = 0;
    for (auto color: enum_range<PixelColor>)
        total_brightness += pixel[color];

    uint8_t average_brightness = total_brightness/3;
    for (auto color: enum_range<PixelColor>)
        pixel[color] = average_brightness;
}    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多