【问题标题】:Java-like enumerations in C++: initialization of static class membersC++ 中的类 Java 枚举:静态类成员的初始化
【发布时间】:2018-06-03 17:55:50
【问题描述】:

我试图模仿 Java 枚举。这是我想出的:

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

它是枚举的基类,它在构造函数中注册指向派生类型实例的指针——通过这种方式,它使静态方法get() 能够通过分配的值访问枚举。这是一个派生类:

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} {}
};

后跟源文件:

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

似乎可行,但恐怕无法保证派生类中的静态成员在第一次调用基类中的get() 之前通过构造函数进行初始化和注册。例如:是否会发生以下情况:调用get() 时基类中的地图为空?

【问题讨论】:

  • 变量UNSIGNED_INT是在动态初始化阶段创建的。这是在调用main之前。因此,如果您不在另一个 ctor 中访问 get(),那么您是安全的。
  • 不要尝试用 C++ 编写 Java,反之亦然。它们是非常不同的语言。您可能持有的关于一种语言的假设将适用于另一种语言。您需要从头开始学习新语言,并忘记您对旧语言的了解。语法看似相似,但不要让它愚弄你; 语义即使对于相似的语法也是不同的。
  • @JesperJuhl 像这样的枚举类对于存储与多个值相关联并且可由其中一个值识别的常量不是很有用吗? c++ 枚举允许每个枚举只存储一个值 - 更不用说您不能按值要求其中之一。
  • @AustinReuter 你总是使用尾随返回类型有什么原因吗?看起来有点不方便。
  • @kiloalphaindia 一旦你习惯了它就不会不方便。我这样做是为了与情况保持一致,当我必须使用或当我想跳过内联 getter 中的返回类型时。

标签: c++ enums static initialization


【解决方案1】:

正如我之前的评论中所述,静态变量(枚举值)在动态初始化阶段进行初始化,即在调用main 函数之前。因此,只要您在 main 函数开始之前不使用“枚举”,您就安全了。 下面演示了一个可能发生这种情况的案例:

#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**){}

当你运行这个程序你会得到:

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

【讨论】:

    猜你喜欢
    • 2015-05-18
    • 1970-01-01
    • 2011-07-18
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多