对非常量成员使用 constexpr 是否有效

Is the use of constexpr valid with non-const members

本文关键字:constexpr 是否 有效 非常 常量 成员      更新时间:2023-10-16

Background

想要实现的目标:我正在尝试实现类似Java枚举的东西(即具有一些附加功能的枚举(。我想出了一个使用两个类的解决方案,其中一个类表示一个值,另一个类使用静态变量作为可能值的枚举来表示每个值。我希望它真正替代枚举,包括在模板实例化中使用枚举值的可能性。为此,枚举值需要是一个常量表达式(constexpr(。但是,我不确定我是否正确使用了 constexpr。

《守则》

这是我想出的代码:

class EnumType {
public:
    enum Enum {val_A, val_B, val_C};
    friend class EnumTypeList;
    EnumType()
    : m_ID(val_A), m_foo(0) {}
    constexpr operator Enum() const {return m_ID;};
    constexpr unsigned int getFoo() const {return m_foo;};
protected:
    constexpr EnumType(const Enum v, const int foo)
    : m_ID(v), m_foo(foo) {}
private:
    Enum m_ID;
    int m_foo;
};
class EnumTypeList {
public:
    static constexpr EnumType A = EnumType(EnumType::val_A, 5);
    static constexpr EnumType B = EnumType(EnumType::val_B, 4);
    static constexpr EnumType C = EnumType(EnumType::val_C, 8);
};

EnumType保存每个值的信息并提供一些附加函数(这里它存储可以使用getFoo()函数访问的附加值m_foo(。枚举本身由包含静态 constexpr 变量的EnumTypeList表示,其中每个变量表示枚举的可能值。这段代码允许我使用 EnumTypeList 中的变量代替EnumType::Enum. 即使在模板中,如以下代码所示:

template <EnumType::Enum T>
class Test {
public:
    void test() {
        std::cout << "Enum is not A" << std::endl;
    }
};
template <>
class Test<EnumType::val_A> {
public:
    void test() {
        std::cout << "Enum is A" << std::endl;
    }
};
int main() {
    Test<EnumTypeList::A> a;
    a.test();
    Test<EnumTypeList::B> b;
    b.test();
    // this shouldn't compile
    /*EnumType x = EnumTypeList::C;
    Test<x> c;
    c.test();*/
}

这如我所期望的那样工作——如上所述,我可以在模板实例化中使用 EnumTypeList 中的值代替EnumType::Enum,但我不能用EnumType x = EnumTypeList::C;

问题所在

虽然代码在 gcc 和 clang 下没有任何警告的情况下正确编译,但我不确定我是否正确使用了 constexpr。问题是,虽然EnumType构造函数和转换运算符operator Enum()是constexpr,但它们都访问变量m_IDm_foo不是常量(因为我需要赋值运算符(。

这很好,文本类型的成员是可修改的。

为了在常量表达式中使用类型,您必须使用常量参数构造它,但这没关系,因为您可以这样做;

static constexpr EnumType A = EnumType(EnumType::val_A, 5);

构造的对象是一个有效的常量表达式,因此可用于初始化constexpr变量A。 您不修改对象的成员,因此它们是否可修改并不重要。

特别是Clang对常量表达式非常严格,如果你做错了什么,它会给出错误。

此对象可用于需要常量表达式的情况:

constexpr EnumType A5(EnumType::val_A, 5);

例如

constexpr int i = A5.getFoo();

此对象不能:

EnumType A6(EnumType::val_A, 6);
constexpr int i = A6.getFoo();  // error