在C++ 20中更改作用域枚举的默认值初始化?

huangapple go评论70阅读模式
英文:

Change default value initialization of scoped enum in C++ 20?

问题

In C++20, it's not possible to change the behavior of default value initialization of a scoped enum directly through language features. Scoped enums are designed to have stricter type safety, and they will always initialize to one of their enumerator values, not an arbitrary value like MyEnum::No in your example.

You can, however, create a wrapper class, as you've attempted in your code, to achieve a similar effect. Your current approach is on the right track, but you need to make some adjustments to make it work as expected. Specifically, you want to ensure that your wrapper class acts like a real scoped enum.

Here's an updated version of your code that should help you achieve your desired behavior:

#include <iostream>

using MyEnumRootType = unsigned int;
enum class EMyEnum : MyEnumRootType {
  Yes = 0x1,
  No = 0x2
};

template <typename Enum, Enum DefaultValue>
class EnumWrapper {
public:
  EnumWrapper() : value_(DefaultValue) {}
  EnumWrapper& operator=(Enum e) { value_ = e; return *this; }
  operator Enum() const { return value_; }

private:
  Enum value_;
};

constexpr EMyEnum MyEnumNo = EMyEnum::No; // Define a constant for MyEnum::No

int main() {
  EnumWrapper<EMyEnum, MyEnumNo> a;
  EnumWrapper<EMyEnum, MyEnumNo> b = EMyEnum::Yes; // This will work now
}

In this updated code, we define a constant MyEnumNo that represents the default value of EMyEnum::No. This allows you to use MyEnumNo as the default value for your wrapper class. Now, when you create a MyEnum variable without specifying a value, it will be initialized with MyEnum::No.

英文:

Is it possible to change the behavior of default value initialization of a scoped enum in C++ 20 ?

For exemple, in the following code, I'd like that a MyEnum variable to be initialized automaticatly with MyEnum::No when declared with MyEnum myValue{};

using MyEnumRootType = unsigned int;
using MyEnum = enum class EMyEnum: MyEnumRootType {
  Yes = 0x1,
  No = 0x2
};


int main() {
  const MyEnum myValue{}; // &lt;- 0
  std::cout &lt;&lt; static_cast&lt;MyEnumRootType&gt;(myValue);
}

edit:
I'v tried to write a wrapper like Benjamin Buch suggested (thanks).
I'm using template because I want it to be reusable.
I managed to handle a default value but I'm struggling to make it act like a real scoped enum (last line of main doesn't compile).

#include &lt;concepts&gt;

using MyEnumRootType = unsigned int;
enum class EMyEnum: MyEnumRootType {
  Yes = 0x1,
  No = 0x2
};

template&lt;typename T&gt;
concept ScopedEnum = std::is_enum_v&lt;T&gt; &amp;&amp; !std::is_convertible_v&lt;T, int&gt;;

template&lt;typename Enum, auto DefaultValue&gt;
  requires ScopedEnum&lt;Enum&gt;
class EnumWrapper{
public:
  EnumWrapper(): value_(DefaultValue) {}
  EnumWrapper&amp; operator=(Enum e) { value_ = e; return *this; }
  operator Enum() const { return value_; }
private:
  Enum value_;
};

using MyEnum = EnumWrapper&lt;EMyEnum, EMyEnum::No&gt;;

int main() {
  MyEnum a{};
  MyEnum b = MyEnum::Yes; // how can I make this works ?
}

Can I do something like this in a template https://en.cppreference.com/w/cpp/language/enum#Using-enum-declaration ?

答案1

得分: 3

这对于枚举来说是不可能的直接操作的(除非你愿意将No的值更改为0x0)。枚举不是类类型(即使在enum class中有class关键字),因此你无法像在类的成员函数/构造函数/析构函数等中那样影响它们的行为。

相反,你需要创建一个类包装器,在其中你可以定义一个默认构造函数来执行你想要的操作。

英文:

That's not possible directly for an enumeration (except if you are willing to change the value of No to 0x0). Enumerations are not class types (even if there is class in enum class), so you can't affect their behavior like you can with member functions/constructors/destructors/etc of classes.

Instead you'll have to make a class wrapper around it in which you can define a default constructor to do whatever you want.

答案2

得分: 2

只有通过编写包装类才能实现这种行为。使用 using enum 可以通过包装类直接访问枚举的值。定义一个枚举类型的数据成员和一个用于该类型的强制类型转换操作符。

using MyEnumRootType = unsigned int;
enum class EMyEnum: MyEnumRootType {
  Yes = 0x1,
  No = 0x2
};

struct MyEnum {
    using enum EMyEnum;

    constexpr operator EMyEnum() const noexcept {
        return value;
    }

    EMyEnum value = No;
};

int main() {
    constexpr MyEnum myValue{};
    static_assert(myValue != MyEnum::Yes);
    static_assert(myValue == MyEnum::No);
    static_assert(myValue != EMyEnum{});
}
英文:

It is only possible to write a wrapper class to achieve this behavior. Use using enum to address the values of your enumeration directly via the wapper class. Define a data member of the enumeration type and a cast operator for this type.

using MyEnumRootType = unsigned int;
enum class EMyEnum: MyEnumRootType {
  Yes = 0x1,
  No = 0x2
};

struct MyEnum {
    using enum EMyEnum;

    constexpr operator EMyEnum() const noexcept {
        return value;
    }

    EMyEnum value = No;
};

int main() {
    constexpr MyEnum myValue{};
    static_assert(myValue != MyEnum::Yes);
    static_assert(myValue == MyEnum::No);
    static_assert(myValue != EMyEnum{});
}

huangapple
  • 本文由 发表于 2023年2月18日 17:18:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75492334.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定