【问题标题】:enum class constructor c++ , how to pass specific value?枚举类构造函数c ++,如何传递特定值?
【发布时间】:2019-12-12 05:46:50
【问题描述】:

我来自 Java,在这里我们可以选择为构造函数设置值。

例子

enum TYPE
{
    AUTO("BMW"),
    MOTOCYCLE("Kawasaki");

    private String mBrandName = "";

    TYPE(final String iBrandName)
    {
        mBrandName = iBrandName;
    }

    public String getBrandName()
    {
        return mBrandName;
    }

    static TYPE getMotocycle()
    {
        return MOTOCYCLE;
    }

    static TYPE getAuto()
    {
        return AUTO;
    }
}

用法

String motoBrand = TYPE.getMotocycle().getBrandName(); // == BMW
String autoBrand = TYPE.getAuto().getBrandName(); // == Kawasaki

所以,想法是你可以给构造函数特定的值(int,String 不管)然后得到它。因此,您有订单号以及您设置的特定值...

问题是,从文档https://docs.microsoft.com/en-us/cpp/cpp/enumerations-cpp?view=vs-2019我了解到cpp中没有这样的选项,对吗?

P.S. 我需要枚举的原因,因为您保存了所有枚举功能(例如元素计数或按数字获取元素),另外您还可以通过构造函数获得更多。

在 Java 中,我可以通过这种方式获取元素数量 TYPE.values().length https://stackoverflow.com/a/17492102/5709159

在 Java 中,我可以通过这种方式通过数字获取元素 TYPE.values()[ordinal] https://stackoverflow.com/a/609866/5709159

【问题讨论】:

  • 普通类也可以实现类似的功能。
  • 枚举类不是一个类,它只是一个强类型枚举。
  • 用一个类就行了,这种Java枚举基本上已经是一个类了。
  • enum 没有“元素计数或按数字获取元素”功能
  • 也许您对enum 关键字感到困惑。 C++ enum(或 enum class)与 Java enum 几乎没有任何共同之处。 C++ enum 本质上只是一个单独的整数类型,可以选择带有一些众所周知的常量值。

标签: c++ enums enumeration


【解决方案1】:

C++ 不是 Java!每种语言都有自己的技术,这些技术非常适合该语言。不要试图用另一种语言在相同(但被破坏的)结构中模仿一种语言的完美结构。

以下是我将如何在 C++ 中解决您的问题:

 // Define the actual enumeration
 enum class [[nodiscard]] Vehicle : unsigned char
 {
     CAR,
     MOTORCYCLE,
     SIZE [[maybe_unused]]
 };

// Convert your enumeration to a string (view)
#include <cassert>
#include <string_view>
[[nodiscard]] constexpr auto to_string(Vehicle v) noexcept -> std::string_view {
  assert(v != Vehicle::SIZE);
  switch (v) {
    case Vehicle::CAR:
      return "Car";
    case Vehicle::MOTORCYCLE:
      return "Motorcycle";
  }
}

要使用它,您可以执行以下操作:

 for (unsigned char c = 0; c < static_cast<unsigned char>(Vehicle::SIZE); ++c)
        std::cout << to_string(static_cast<Vehicle>(c)) << std::endl;

每次都编写这个有点麻烦,但是,您可以编写自己的模板类来帮助对其进行迭代。例如:

#include <type_traits>
// The generic stuff you only write once
// Assumes you don't assign any values to your enumeration by hand + it ends on
// 'SIZE' (unless a second argument was passed if a different name was used)
template <typename TEnumeration, TEnumeration TSize = TEnumeration::SIZE>
class [[nodiscard]] EnumRange final {
  using type = std::underlying_type_t<TEnumeration>;

 public:
  // The iterator that can be used to loop through all values
  //
  class [[nodiscard]] Iterator final {
    TEnumeration value{static_cast<TEnumeration>(0)};

   public:
    constexpr Iterator() noexcept = default;
    constexpr Iterator(TEnumeration e) noexcept : value{e} {}

    constexpr auto operator*() const noexcept -> TEnumeration { return value; }
    constexpr auto operator-> () const & noexcept -> const TEnumeration* {
      return &value;
    }
    constexpr auto operator++() & noexcept -> Iterator {
      value = static_cast<TEnumeration>(1 + static_cast<type>(value));
      return *this;
    }

    [[nodiscard]] constexpr auto operator==(Iterator i) -> bool { return i.value == value; }
    [[nodiscard]] constexpr auto operator!=(Iterator i) -> bool { return i.value != value; }
  };

  constexpr auto begin() const noexcept -> Iterator { return Iterator{}; }
  constexpr auto cbegin() const noexcept -> Iterator { return Iterator{}; }

  constexpr auto end() const noexcept -> Iterator { return Iterator{TSize}; }
  constexpr auto cend() const noexcept -> Iterator { return Iterator{TSize}; }

  [[nodiscard]] constexpr auto size() const noexcept -> type {
    return static_cast<type>(TSize);
  }
};

用法:

#include <iostream>
int main(int, char**) {
  auto range = EnumRange<Vehicle>{};
  std::cout << static_cast<int>(range.size()) << std::endl;
  for (auto v : range) std::cout << to_string(v) << std::endl;
}

正如您在第一个测试代码中看到的,您可以使用static_cast 从数值转换为枚举。但是,它假定您有一些对枚举有效的值。使用相同的范围假设,我们可以编写自己的检查变量:

#include <stdexcept>
#include <type_traits>

template <typename TEnumeration, TEnumeration TSize = TEnumeration::SIZE>
[[nodiscard]] constexpr auto checked_enum_cast(
    std::underlying_type_t<TEnumeration> numeric_value) noexcept(false)
    -> TEnumeration {
        using type = std::underlying_type_t<TEnumeration>;
  if constexpr (std::is_signed_v<type>)
    if (numeric_value < 0) throw std::out_of_range{"Negative value"};

  if (numeric_value >= static_cast<type>(TSize)) throw std::out_of_range{"Value too large"};

  return static_cast<TEnumeration>(numeric_value);
}

要使用这个,你可以写:

  try {
    std::cout << to_string(checked_enum_cast<Vehicle>(1)) << std::endl;
    std::cout << to_string(checked_enum_cast<Vehicle>(2)) << std::endl;
  } catch (const std::out_of_range& e) {
    std::cout << e.what() << std::endl;
  }

注意:如果要生活在一个没有异常的世界中,可以返回 std::nullopt 并将返回类型改为 std::optional&lt;TEnumeration&gt;

All code combined + execution at Compiler Explorer

请注意,迭代器可以精炼,但是,我不是细节方面的专家。 (对于循环,没关系,如果你想将它用于算法,它可以)

【讨论】:

  • 注意:由于我不熟悉 Java 枚举,我可能错过了一些功能。如果是这样,请让我知道缺少什么,然后我会更新答案。
【解决方案2】:

在 C++ 中必须创建一个类:

class TYPE 
{
public:
    static const TYPE AUTO;
    static const TYPE MOTOCYCLE;

private:
    std::string mBrandName;

    TYPE(std::string iBrandName)
        : mBrandName(iBrandName)
    {}

    TYPE(const TYPE&) = default;
    TYPE(TYPE&&)      = default;
    TYPE& operator=(const TYPE&) = default;
    TYPE& operator=(TYPE&&) = default;
    ~TYPE() = default;

public:
    std::string getBrandName() { return mBrandName; }

    static TYPE getMotocycle() { return MOTOCYCLE; }
    static TYPE getAuto() { return AUTO; }
};

const TYPE TYPE::AUTO("BMW");
const TYPE TYPE::MOTOCYCLE("Kawasaki");

但这没有枚举的好处(自动编号、排序、转换……)

【讨论】:

  • 也许更好的解决方案是将枚举包装在一个类中?
  • 好的,但是您如何获取类型的数量? (AUTO,MOTO 是 2)
  • 首先这个例子不会编译。在类中初始化父类型的静态类字段是非法的。 @AlekseyTimoshchenko 您将需要手动实现所有这些功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
相关资源
最近更新 更多