15 c++ inheritance method-chaining
考虑:
// member data omitted for brevity
// assume that "setAngle" needs to be implemented separately
// in Label and Image, and that Button does need to inherit
// Label, rather than, say, contain one (etc)
struct Widget {
Widget& move(Point newPos) { pos = newPos; return *this; }
};
struct Label : Widget {
Label& setText(string const& newText) { text = newText; return *this; }
Label& setAngle(double newAngle) { angle = newAngle; return *this; }
};
struct Button : Label {
Button& setAngle(double newAngle) {
backgroundImage.setAngle(newAngle);
Label::setAngle(newAngle);
return *this;
}
};
int main() {
Button btn;
// oops: Widget::setText doesn't exist
btn.move(Point(0,0)).setText("Hey");
// oops: calling Label::setAngle rather than Button::setAngle
btn.setText("Boo").setAngle(.5);
}
Run Code Online (Sandbox Code Playgroud)
有什么技巧可以解决这些问题吗?
示例:使用模板魔术使Button :: move返回Button等.
编辑很明显,第二个问题是通过使setAngle虚拟解决的.
但第一个问题仍未以合理的方式解决!
编辑:嗯,我想在C++中做不到是不可能的.无论如何,谢谢你的努力.
Kon*_*lph 15
您可以扩展CRTP来处理此问题.monjardin的解决方案朝着正确的方向发展.您现在需要的只是一个默认Label
实现,将其用作叶类.
#include <iostream>
template <typename Q, typename T>
struct Default {
typedef Q type;
};
template <typename T>
struct Default<void, T> {
typedef T type;
};
template <typename T>
void show(char const* action) {
std::cout << typeid(T).name() << ": " << action << std::endl;
}
template <typename T>
struct Widget {
typedef typename Default<T, Widget<void> >::type type;
type& move() {
show<type>("move");
return static_cast<type&>(*this);
}
};
template <typename T = void>
struct Label : Widget<Label<T> > {
typedef typename Default<T, Widget<Label<T> > >::type type;
type& set_text() {
show<type>("set_text");
return static_cast<type&>(*this);
}
};
template <typename T = void>
struct Button : Label<Button<T> > {
typedef typename Default<T, Label<Button<T> > >::type type;
type& push() {
show<type>("push");
return static_cast<type&>(*this);
}
};
int main() {
Label<> lbl;
Button<> btt;
lbl.move().set_text();
btt.move().set_text().push();
}
Run Code Online (Sandbox Code Playgroud)
也就是说,考虑这样的努力是否值得小额添加语法奖励.考虑其他解决方案
对于第二个问题,使setAngle虚拟化可以解决问题.
对于第一个,没有简单的解决方案.Widget :: move返回一个Widget,它没有setText方法.你可以创建一个纯虚拟的setText方法,但这是一个非常难看的解决方案.你可以在按钮类上重载move(),但这很难维护.最后,您可以使用模板执行某些操作.也许是这样的:
// Define a move helper function
template <typename T>
T& move(T& obj, Point& p){ return obj.move(p); };
// And the problematic line in your code would then look like this:
move(btn, Point(0,0)).setText("Hey");
Run Code Online (Sandbox Code Playgroud)
我会让你决定哪种解决方案最干净.但是,为什么你需要能够链接这些方法有什么特别的原因吗?