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?
Je voudrais:
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};
}