web-dev-qa-db-fra.com

Travailler autour de la limitation de «Constexpr» Membre de données statiques de même type que la classe Englobant

Je veux donner constexpr capacités à une classe Color une classe qui ressemble à ceci:

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

Mon désir est de garder l'API de cette classe inchangée, je voudrais donc supprimer complètement color.cpp et apporter ces modifications au fichier d'en-tête:

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

Toutefois, le code ci-dessus ne compile pas comme constexpr Les éléments de données statiques avec le même type que la classe jointe ne sont pas autorisés dans C++ .

Bien sûr, je pourrais changer l'API en quelque chose comme ColorConstants::Red et déplacez l'objet Red de la classe, mais je ne veux pas casser les utilisateurs existants.

La seule solution de contournement que j'ai pensée ressemble à ceci:

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

La solution de contournement ci-dessus permet la plupart des codes existants qui utilisent Color pour compiler toujours après les modifications, mais cela échoue évidemment chaque fois que le Red n'est pas utilisé dans un contexte où une conversion implicite sur Color est requis.

Donc, ma question est la suivante: est-il possible de contourner la limitation constexpr vu ci-dessus, tournant Red dans une expression constante, tout en conservant toujours l'original Color::Red Syntaxe et éviter de casser le code existant?

2
Vittorio Romeo

Je voudrais:

  • séparez les constantes dans une autre classe
  • mettre la représentation de couleur préférée dans une classe dans un espace de noms
  • créer une nouvelle classe qui recrée l'ancienne API dans un autre espace de noms mais avec une déclaration ou similaire

C'est plus de code que @artyer Réponse, mais cela vous permettrait de migrer les utilisateurs du placement constant incompatible avec la langue comme tout en préservant l'ancien code. Une fois la migration terminée, vous pouvez surtout supprimer le code au nettoyage.

Quelque chose comme:

namespace v2 {
struct Color
{
    constexpr Color(int r, int g, int b, int a) : r_(r), g_(g), b_(b), a_(a) {}
    int r_, g_, b_, a_;
};

}

// todo: get rid of v1::Color use and make this a namespace 
struct ColorConstants {
       static constexpr v2::Color Red{255, 0, 0, 255};
};

inline namespace v1 {
struct Color : v2::Color, ColorConstants {
    using v2::Color::Color;
    constexpr Color(v2::Color const& base) :v2::Color(base) {}
};
}


int
main()
{
    constexpr Color light_color = Color::Red;
    constexpr Color Nice_color {255,165,0,255};
}

https://godbolt.org/z/ar497eyym

0
Bowie Owens