枚举类构造函数 c++ ,如何传递特定值?

2021-12-29 00:00:00 enums enumeration c++

我来自 Java,这里我们有设置值到构造函数这样的选项.

I came from Java and here we have such option as set value to constuctor.

示例

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 什么的)然后得到它.因此,您有订单号以及您设置的特定值...

So, idea is that you can give to constructor specific value (int, String whatever) and then get it. So, you have order number as well as specific value that you set to...

问题是,来自文档 https://docs.microsoft.com/en-us/cpp/cpp/enumerations-cpp?view=vs-2019 我知道cpp中没有这个选项,对吗?

Question is, from documentation https://docs.microsoft.com/en-us/cpp/cpp/enumerations-cpp?view=vs-2019 I understood that there is no such option in cpp, is it right?

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

P.S. Reason why I need enum, because you save all enum functionality(like count of elements or get element by number) and additionally you get a little bit more with constructor.

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

In Java I can get count of elements this way TYPE.values().length https://stackoverflow.com/a/17492102/5709159

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

In Java I can get element by number this way TYPE.values()[ordinal] https://stackoverflow.com/a/609866/5709159

推荐答案

C++ 不是 Java!每种语言都有自己的技术,这些技术非常适合该语言.不要试图用另一种语言的相同(但已损坏)的构造来模仿一种语言的完美构造.

C++ ain't Java! Every language has its own techniques which are a good fit with the language. Don't try to mimic a perfectly fine construct of one language in the same (but broken) construct in a different language.

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

Here is how I would solve your issue in 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";
  }
}

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

To use it, you can do something like:

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

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

Writing this every time is a bit cumbersome, however, you could write your own template class that helps with iterating over it. For example:

#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 将数值转换为枚举.但是,它假定您有一些对枚举有效的值.使用相同的范围假设,我们可以编写我们自己的检查变体:

As you saw in the first test code, you can go from a numeric value to an enumeration by using static_cast. However, it assumes that you have some value that is valid for the enumeration. With the same assumptions of the range, we can write our own checked variant:

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

要使用这个,你可以写:

To use this, you can write:

  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相反.

Note: If one would live in an exception-free world, one could return std::nullopt and change the return type to std::optional<TEnumeration> instead.

所有代码组合 + 在编译器资源管理器中执行

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

Please note that the iterator can be refined, however, I ain't an expert in the details. (and for looping, it doesn't matter, if you ever want to use it for an algorithm it could)

相关文章