【问题标题】:Defining static constexpr values in an enum like wrapper class在枚举中定义静态 constexpr 值,如包装类
【发布时间】:2017-12-26 23:46:52
【问题描述】:

我有一个经常用于枚举包装器的类,但这需要一个 cpp 文件。谁能告诉我如何使用 constexpr

头文件如下:

// Extend-able in the future
class CDUVariable {
public:
    enum Value {
        baud, firmware, type, serial
    };
    static const CDUVariable Baud, Firmware, Type, Serial;

    /// Comparison operator (used for strict weak ordering).
    bool operator<(const CDUVariable& rhs) const {
        return mValue < rhs.mValue;
    }

    /// Integral operator cast for switch statements (cast to named enum).
    operator const Value() const {
        return mValue;
    }

    /// Serialized version of the enum.
    std::string getStringVal() const {
        return mStringVal;
    }
    static const std::set<CDUVariable>& getValues() {
        static std::set<CDUVariable> gValues;
        if (gValues.empty()) {
            gValues.insert(Baud);
            gValues.insert(Firmware);
            gValues.insert(Type);
            gValues.insert(Serial);
        }
        return gValues;
    }
    static CDUVariable valueOf(const std::string& rStringVal) {
        for (const auto& next : getValues()) {
            if (next.getStringVal() == rStringVal) {
                return next;
            }
        }
        throw std::invalid_argument(
            "Illegal Argument: " + rStringVal);
    }
private:
    CDUVariable(const Value& rValue, const std::string& rStringVal)
        : mValue(rValue)
        , mStringVal(rStringVal)
    {}
    Value mValue;
    std::string mStringVal;
};

而cpp文件是:

const CDUVariable CDUVariable::Baud(baud, "0");
const CDUVariable CDUVariable::Firmware(firmware, "1");
const CDUVariable CDUVariable::Type(type, "2");
const CDUVariable CDUVariable::Serial(serial, "3");

我想知道是否可以使用新的 constexpr 语法来初始化头文件中的所有内容。我的语法有问题。

我尝试如下修改标题的前缀:

// Extend-able in the future
class CDUVariable {
public:
    constexpr enum Value {
        baud, firmware, type, serial
    };
    constexpr static CDUVariable Baud(baud, "0") Firmware(firmware, "1"), Type(type, "2"), Serial(serial, "3");

但这最终给了我一堆错误。我想知道如何迁移到新的 constexpr 语法,这样做的好处是我可以发布一个仅包含这些枚举类型类的标头库。

2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C2061: syntax error: identifier 'baud'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C3646: 'Firmware': unknown override specifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(107): error C2059: syntax error: '('
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2143: syntax error: missing ')' before ';'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2098: unexpected token after data member 'mValue'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(147): error C2059: syntax error: ')'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(111): error C2065: 'mValue': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(111): error C2039: 'mValue': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(116): error C2065: 'mValue': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(126): error C3867: 'CDUVariable::Baud': non-standard syntax; use '&' to create a pointer to member
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(127): error C2065: 'Firmware': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(128): error C2065: 'Type': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(129): error C2065: 'Serial': undeclared identifier
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(146): error C2614: 'CDUVariable': illegal member initialization: 'mValue' is not a base or member
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): error C2350: 'CDUVariable::Baud' is not a static member
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): note: see declaration of 'CDUVariable::Baud'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(165): error C2248: 'CDUVariable::CDUVariable': cannot access private member declared in class 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(143): note: see declaration of 'CDUVariable::CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(166): error C2039: 'Firmware': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(166): error C2065: 'firmware': undeclared identifier
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(167): error C2039: 'Type': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(167): error C2065: 'type': undeclared identifier
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(168): error C2039: 'Serial': is not a member of 'CDUVariable'
2>c:\users\johnc\main\app739\include\vcdu\vcduconstants.h(102): note: see declaration of 'CDUVariable'
2>c:\users\johnc\main\app739\src\vcdu\vcduconstants.cpp(168): error C2065: 'serial': undeclared identifier
2>Generating Code...

【问题讨论】:

  • 您是否曾经想要复制或分配 CDUVariable?典型的用例是什么? (我怀疑,我们可能正在处理一个 XY 问题。)
  • @Walter 典型的用例是将这些 Enum 类型类视为它们的 java 等价物。这个想法是我可以做类似'auto cduVariable = CDUVariable(CDUVariable::valueOf(static_cast<:value>(data))'这样的事情,我可以从原始数据中查找一个枚举。我还可以getStringVal并发送一个漂亮的打印版本到日志。我尝试将 std::string 更改为 const char* 以解决非 constexpr std::string 但我无法获得仅标头版本来编译。

标签: c++ c++11 enums c++14 constexpr


【解决方案1】:

我有一个经常用于枚举包装器的类,但这需要一个 cpp 文件。谁能告诉我如何使用 constexpr

要将您的示例减少到最小,请考虑以下类:

struct Example {
    enum Value { A, B };
    static constexpr Example Foo{Value::A, "bar" };
    constexpr Example( Value v, std::string s): v{v}, s{s} {}

private:
    Value v;
    std::string s;
};

这或多或少是您上次尝试的简化版本。
能够构建Exampleconstexpr 版本所需的几乎所有部分都已准备就绪,但事实上:

  • static constexpr 数据成员必须初始化,但ExampleFoo 的声明点是不完整的类型。

  • 所有数据成员都必须是(让我说)constexpr 可构造的并且std::string 没有可以使用的constexpr 构造函数。

因此,您的尝试将失败,这一点毫无疑问。

此外,请注意,除非您使用 C++17 或您在 .cpp 文件中单独定义它们,否则您不能使用 constexpr 数据成员,就像您之前所做的那样。这是因为这些成员没有在 C++11/14 中隐式定义。
这意味着您甚至不能将它们与std::set::insert 一起使用,因为它只接受引用。至少,你不能使用它们,除非你在.cpp 文件中定义,当然。

那么,回到问题:

谁能告诉我如何使用 constexpr

不,我们不能,也不值得。在我看来,您需要 odr 可用的数据成员,所以继续使用 static 并将定义放在专用文件中。

【讨论】:

    【解决方案2】:

    根据定义,枚举值是常量,毕竟你不能在运行时添加值。这就是为什么不允许在枚举声明之前放置 constexpr 的原因之一,这是没有意义的,因为它不会改变 enum 的任何内容:

    constexpr enum Value { baud, firmware, type, serial };
    ^^^^^^^^^
     illegal
    

    附带说明,您现在不能构造 constexpr CDUVariable,因为它的构造函数不是 constexpr。如果您尝试适当地标记它,它将失败,因为mStringVal 没有constexpr 构造函数,并且每个变量都必须初始化。

    如果您只打算存储单个字符,请使用char,它作为一个原语,可以在编译时构建。

    【讨论】:

    • 根据定义,枚举已经是 constexpr
    • @skypjack 嗯,是的,但是那些命名常量是constexpr,因为它们在编译时是固定的。从我的句子中不是很清楚吗?我应该怎么说呢?因为我现在很清楚。但我不是本地人。
    • 它们比constexpr 更恒定(以语言定义它们的方式)。
    • @skypjack 好的。谢谢(你的)信息! :)
    • 不客气。有关详细信息,请参阅here。如您所见,枚举是具有命名常量的独特类型枚举器列表中的标识符被声明为常量,并且可以出现在需要常量的任何地方constexpr 甚至没有在这里提到,由语言指定。
    【解决方案3】:

    您可以通过以下方式摆脱这些静态变量:

    static const std::set<CDUVariable>& getValues() {
        static const std::set<CDUVariable> gValues = {
            { baud, "0"}, // or Baud(),
            { firmware, "1"},
            { type, "2"},
            { serial, "3"}
        };
        return gValues;
    }
    

    static const CDUVariable& Baud() { static const CDUVariable var{ baud, "0"}; return var; }
    

    static CDUVariable Baud() { return { baud, "0"}; }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-20
      • 2014-06-29
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多