使用单个参数来编码其类型中的特定类型的操作,而不是模板参数。
class Color {
private:
uint8_t r_;
uint8_t g_;
uint8_t b_;
public:
Color(uint8_t r, uint8_t g, uint8_t b):
r_{r}, g_{g}, b_{b} {};
};
class Opacity {
private:
uint8_t alpha_;
public:
Opacity(uint_8 alpha): alpha_{alpha} {};
};
class Rectangle {
private:
uint32_t x_;
uint32_t y_;
uint32_t w_;
uint32_t h_;
public:
Rectangle(uint32_t x, uint32_t y, uint32_t w, uint32_t h):
x_{x}, y_{y}, w_{w}, h_{h} {};
};
// etc.
然后使用函数重载而不是模板。
class Context {
public:
Set(const Color& c);
Set(const Opacity& o);
Draw(const Rectangle& rect);
// etc.
}
使用情况至少非常接近您尝试实现的目标。
Set(Color{255, 255, 255});
Set(Opacity{128});
Draw(Rectangle{0, 0, 50, 50});
Set(Opacity{255});
Draw(Texture{Textures::Woman, 75, 75});
您还可能希望有不同的 Rectlangle 构造函数,一个有两个点,一个有一个点和宽度和高度。在那里你可以使用相同的策略:
class Width {
private:
uint32_t w_
public:
Width(uint32_t w): w_{w} {};
operator uint32_t () { return w_; };
};
class Height {
// same as Width
};
class Rectangle {
private:
uint32_t x1_;
uint32_t y1_;
uint32_t x2_;
uint32_t y2_;
public:
Rectangle(uint32_t x, uint32_t y, Width w, Height h):
x1_{x}, y1_{y}, x2_{x+w}, y2_{y+h} {};
Rectangle(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2):
x1_{x1}, y1_{y1}, x2_{x2}, y2_{y2} {};
};
// usage
Draw(Rectangle{0, 0, Width{50}, Height{50}}); // use width and height
Draw(Rectangle{0, 0, 50, 50}); // use two points
你甚至可以更进一步,定义一个 Point 类型,这样绘制一个矩形就会像这样:
Draw(Rectangle{Point{0, 0}, Width{50}, Height{50}}); // use width and height
Draw(Rectangle{Point{0, 0}, Point{50, 50}}); // use two points
使用类似的技术在表达意图和减少噪音之间找到完美的平衡。
当然,您也可以在 Context 中添加简单的模板函数:
template<typename T, typename A...>
Set(A&& args...) {
Set(T{std::forward(args)...});
}
template<typename T, typename A...>
Draw(A&& args...) {
Draw(T{std::forward(args)...});
}
现在您可以按照最初的预期使用整台机器了。
Set<Color>(255, 255, 255);
Set<Opacity>(128);
Draw<Rectangle>(0, 0, 50, 50);
Set<Opacity>(255);
Draw<Texture>(Textures::Woman, 75, 75);
// With width and height types
Draw<Rectangle>(0, 0, Width{50}, Height{50});
Draw<Rectangle>(0, 0, 50, 50);
// With Point Type
Draw<Rectangle>(Point{0, 0}, Width{50}, Height{50});
Draw<Rectangle>(Point{0, 0}, Point{50, 50});