Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working around limitation of `constexpr` static data member with same type as enclosing class [duplicate]

I want to give constexpr capabilities to a Color class that looks like this:

// 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);
// ...

My desire is to keep the API of this class unchanged, therefore I'd like to completely remove color.cpp and make these changes to the header file:

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

However, the code above does not compile as constexpr static data members with the same type as the enclosing class are not allowed in C++.

Of course, I could change the API to something like ColorConstants::Red and move the Red object out of the class, but I do not want to break existing users.

The only workaround I've thought of looks like this:

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

The above workaround allows most existing code that uses Color to still compile after the changes, but it obviously fails whenever the Red is not used in a context where an implicit conversion to Color is required.

So, my question is: is it possible to work around the constexpr limitation seen above, turning Red into a constant expression, while still retaining the original Color::Red syntax and avoiding breaking existing code?

like image 421
Vittorio Romeo Avatar asked Sep 06 '25 02:09

Vittorio Romeo


1 Answers

The way to do this is to have the declaration be simply const, but have an out-of-line definition that is inline constexpr, like so:

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.
like image 143
Artyer Avatar answered Sep 08 '25 22:09

Artyer