简体   繁体   English

C ++中类似Java的枚举:静态类成员的初始化

[英]Java-like enumerations in C++: initialization of static class members

I tried to mimic Java enumerations. 我试图模仿Java枚举。 Here's what I came up with: 这是我想出的:

template<typename Enumeration_Type>
class Enumeration {
public:
    static auto get(int value) -> const Enumeration_Type& {
        const auto result = getMap().find(value);
        return *dynamic_cast<const Enumeration_Type*>(result->second);
    };

    const int value;

    Enumeration(const Enumeration& other) = delete;
    Enumeration(Enumeration&& other) noexcept = delete;
    auto operator=(const Enumeration& other) -> Enumeration& = delete;
    auto operator=(Enumeration&& other) noexcept -> Enumeration& = delete;

    virtual operator int() const noexcept { return value; }
protected:
    explicit constexpr Enumeration(const int value)
        : value{value} { getMap().emplace(value, this); }

    ~Enumeration() = default;
private:
    static auto getMap() noexcept -> std::unordered_map<int, const Enumeration<Enumeration_Type>*>& {
        static std::unordered_map<int, const Enumeration<Enumeration_Type>*> map;
        return map;
    }
};
}

It's a base class for enumerations, it registers pointers to instances of derived types in the constructor - in this way it enables the static method get() to access enumerations by assigned values. 它是枚举的基类,它在构造函数中注册指向派生类型的实例的指针-通过这种方式,它使静态方法get()可以通过分配的值访问枚举。 And here's a derived class: 这是一个派生类:

class DataType final: public Enumeration<DataType> {
public:
    static const DataType UNSIGNED_INT;
// Other types...

    const std::string_view name;

    using Enumeration<DataType>::get;
private:
    constexpr DataType(const int value, const std::string_view name) noexcept
        : Enumeration<DataType>{value},
          name{name} {}
};

Followed by the source file: 后跟源文件:

const DataType DataType::UNSIGNED_INT{0x1405, "UNSIGNED INT"};
// Other types...

It seems to work, but I'm afraid there might be no guarantee for static members in the derived class to be initialized and registered through the constructor before the first call to get() in the base class. 这似乎可行,但恐怕无法保证派生类中的静态成员在对基类中的第一次调用get()之前通过构造函数进行初始化和注册。 For example: could the following happen: the map in the base class is empty on the call to get() ? 例如:是否可能发生以下情况:调用get()基类中的映射为空?

As already stated in my comment earlier, the static variables (Enum Values) are initialized in the dynamic initialization phase, which is before the main function is called. 如我前面的评论中所述,静态变量(枚举值)是在动态初始化阶段(在调用main函数之前)进行初始化的。 Therefore you are on the safe side as long as you do not use your 'enums' before the main function starts. 因此,只要您在主要功能启动之前不使用“枚举”,就可以保证安全。 The following demonstrates a case, where this can happen: 下面演示了一种情况,在这种情况下可能会发生:

#include <unordered_map>
#include <iostream>
#include <tuple>

class no_such_enum_value_exception {};

template<typename Enumeration_Type>
class Enumeration {
    using MapType = std::unordered_map<int, const Enumeration<Enumeration_Type>*>;
public:
    static const Enumeration_Type& get(int value)
    {
        const MapType& EnumMap = getMap();
        const auto result = EnumMap.find(value);

        if (result ==  EnumMap.end()) throw no_such_enum_value_exception();

        return *dynamic_cast<const Enumeration_Type*>(result->second);
    };

    const int value;

    virtual operator int() const noexcept { return value; }

protected:
    explicit constexpr Enumeration(const int value)
        : value{value} { getMap().emplace(value, this); }

    ~Enumeration() = default;

private:
    static MapType& getMap() noexcept {
        static MapType map;
        return map;
    }

// moved down as they are not an important part of the interface
    Enumeration(const Enumeration& other) = delete;
    Enumeration(Enumeration&& other) noexcept = delete;
    auto operator=(const Enumeration& other) -> Enumeration& = delete;
    auto operator=(Enumeration&& other) noexcept -> Enumeration& = delete;
};


class DataType final: public Enumeration<DataType> {
public:
// Other types...

    const std::string_view name;

    using Enumeration<DataType>::get; // not needed get is available anyway
public:
    constexpr DataType(const int value, const std::string_view name) noexcept
        : Enumeration<DataType>{value},
        name{name} {}
};

class DataType2 final: public Enumeration<DataType2> {
public:
    DataType2(int value)
        : Enumeration<DataType2>{value}
    {
        try {
            const DataType& dt1_b = DataType::get(2);
            std::cout << "DataType::get(2): " << dt1_b.name << std::endl;
        }
        catch (const no_such_enum_value_exception&)
        {
            std::cout << "DataType::get(2) failed" << std::endl;
        }
    }
};

static const DataType DT1_A{1, "UNSIGNED INT"};
static const DataType2 DT2_A{1};
static const DataType DT1_B{2, "UNSIGNED INT"};
static const DataType2 DT2_B{2};


int main(int,char**){}

When you run this program you will get: 当您运行此程序时,您将获得:

DataType::get(2) failed                                                                                                                                                                                                                            
DataType::get(2): UNSIGNED INT                                                                                                                                                                                                                 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM