我想给Color
类赋予constexpr
功能,它看起来像这样:
// 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);
// ...
我的愿望是保持这个类的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};
// ...
};
但是,上面的代码不能编译为C++中不允许使用与封闭类类型相同的constexpr
静态数据成员。
当然,我可以将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};
};
上面的解决方法允许大多数使用Color
的现有代码在更改后仍然进行编译,但如果在需要隐式转换为Color
的上下文中不使用Red
,它显然会失败。
因此,我的问题是:是否可以绕过上述constexpr
限制,将Red
转换为常量表达式,同时仍然保留原始Color::Red
语法并避免破坏现有代码
实现这一点的方法是让声明简单地为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.
我会:
- 将常量分离到另一个类中
- 将首选颜色表示形式放在命名空间中的类中
- 创建一个新类,该类在另一个命名空间中重新创建旧的API,但使用using声明或类似声明
它比@Artyer的答案更多的是代码,但这将为您提供一种方法,让用户远离与语言不匹配的固定位置,同时保留旧代码。一旦迁移完成,您基本上只需删除代码即可进行清理。
类似于:
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