考虑:
// 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); }
有什么技巧可以解决这些问题吗?
示例:使用模板魔术使Button :: move返回Button等.
编辑很明显,第二个问题是通过使setAngle虚拟解决的.
但第一个问题仍未以合理的方式解决!
编辑:嗯,我想在C++中做不到是不可能的.无论如何,谢谢你的努力.
您可以扩展CRTP来处理此问题.monjardin的解决方案朝着正确的方向发展.您现在需要的只是一个默认Label
实现,将其用作叶类.
#includetemplate struct Default { typedef Q type; }; template struct Default { typedef T type; }; template void show(char const* action) { std::cout << typeid(T).name() << ": " << action << std::endl; } template struct Widget { typedef typename Default >::type type; type& move() { show ("move"); return static_cast (*this); } }; template struct Label : Widget
也就是说,考虑这样的努力是否值得小额添加语法奖励.考虑其他解决方案
对于第二个问题,使setAngle虚拟化可以解决问题.
对于第一个,没有简单的解决方案.Widget :: move返回一个Widget,它没有setText方法.你可以创建一个纯虚拟的setText方法,但这是一个非常难看的解决方案.你可以在按钮类上重载move(),但这很难维护.最后,您可以使用模板执行某些操作.也许是这样的:
// Define a move helper function templateT& 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");
我会让你决定哪种解决方案最干净.但是,为什么你需要能够链接这些方法有什么特别的原因吗?