解决与封闭类具有相同类型的“constexpr”静态数据成员的限制

Vit*_*meo 4 c++ static initialization constexpr c++17

constexpr我想为一个类提供Color如下功能:

// color.hpp
struct Color
{
    Color(int r, int g, int b, int a);
    static const Color Red;
    // ...
};



// color.cpp
Color::Color(int r, int g, int b, int a) { /* ... */ }
const Color Color::Red(255, 0, 0, 255);
// ...
Run Code Online (Sandbox Code Playgroud)

我的愿望是保持此类的 API 不变,因此我想完全删除color.cpp并对头文件进行以下更改:

// color.hpp
struct Color
{
    constexpr Color(int r, int g, int b, int a) { /* ... */ }
    inline static constexpr Color Red{255, 0, 0, 255};
    // ...
};
Run Code Online (Sandbox Code Playgroud)

但是,上面的代码不会编译为与constexpr封闭类具有相同类型的静态数据成员,这在 C++ 中是不允许的

当然,我可以将 API 更改为类似的内容ColorConstants::Red并将Red对象移出类,但我不想破坏现有用户。

我想到的唯一解决方法如下所示:

// color.hpp
struct Color 
{
private:
    struct ColorInit 
    {
        int r, g, b, a;
        constexpr ColorInit(int r, int g, int b, int a) { /* ... */ }
        constexpr inline operator Color() const { /* ... */ }
    }

public:
    constexpr Color(int r, int g, int b, int a) { /* ... */ }
    inline static constexpr ColorInit Red{255, 0, 0, 255};
};
Run Code Online (Sandbox Code Playgroud)

上述解决方法允许大多数现有代码Color在更改后仍然可以编译,但只要Red未在隐式转换为的上下文中使用,它显然会失败Color在需要

所以,我的问题是:是否可以解决constexpr上面看到的限制,变成Red常量表达式,同时仍然保留原始Color::Red语法并避免破坏现有代码?

Art*_*yer 6

做到这一点的方法是让声明简单const,但有一个外行定义inline constexpr,如下所示:

struct Color
{
    constexpr Color(int r, int g, int b, int a) { /* ... */ }
    static const Color Red;
    // ...
};

inline constexpr Color Color::Red{255, 0, 0, 255};
// From this point on, Color::Red can be used in constant expressions.
Run Code Online (Sandbox Code Playgroud)

  • @NicolBolas 这仅适用于函数。对于变量,“constexpr”只能显式地位于定义上,而不能位于任何声明上(https://eel.is/c++draft/dcl.constexpr#1.sentence-1)。我第一次了解到这一点是在看到 `<compare>` 如何具有像 `std::strong_ordering` 这样的类型以及相同类型的静态 constexpr 成员时,这就是它的完成方式:https://eel.is/c++草稿/cmp.partialord#lib:partial_ordering (4认同)