Method chaining + inheritance don't play well together?

后端 未结 15 766
北荒
北荒 2020-12-08 22:20

Consider:

// member data omitted for brevity

// assume that \"setAngle\" needs to be implemented separately
// in Label and Image, and that Button does need         


        
相关标签:
15条回答
  • 2020-12-08 23:06

    I think (I haven't tested it) this will do it using templates:

    template<class T> struct TWidget {
        T& move(Point newPos) { pos = newPos; return (T&)*this; }
    };
    
    template<class T> struct TLabel : TWidget<T> { ... }
    
    struct Label : TLabel<Label> { ... }
    
    struct Button : TLabel<Button> { ... }
    

    Notes:

    • Any/every base class needs to be a template, with a separate non-template leaf class at the top (contrast the LabelT and Label classes).
    • The cast could be a dynamic_cast if you like.
    • Instead of casting the "return *this", the base class could contain a T& as a data member (the derived class would pass this to the base class' constructor), which would be an extra data member, but which avoids a cast and I think may permit composition instead of or as well as inheritance.
    0 讨论(0)
  • 2020-12-08 23:07

    You can extend CRTP to handle this. monjardin's solution goes in the right direction. All you need now is a default Label implementation to use it as a leaf class.

    #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();
    }
    

    That said, consider whether such an effort is worth the small added syntax bonus. Consider alternative solutions.

    0 讨论(0)
  • 2020-12-08 23:08

    This compiles on gcc 4.3.2 and is sort of a mixin pattern.

    #include <string>
    
    using namespace std;
    
    struct Point {
        Point() : x(0), y(0) {}
        Point(int x, int y) : x(x), y(y) {}
    
        int x, y;
    };
    
    template <typename T>
    struct Widget {
        T& move(Point newPos) {
            pos = newPos;
            return *reinterpret_cast<T *> (this);
        }
    
        Point pos;
    };
    
    template <typename T>
    struct Label : Widget<Label<T> > {
        T& setText(string const& newText) {
            text = newText;
            return *reinterpret_cast<T *> (this);
        }
        T& setAngle(double newAngle) {
            angle = newAngle;
            return *reinterpret_cast<T *> (this);
        }
    
        string text;
        double angle;
    };
    
    struct Button : Label<Button> {
        Button& setAngle(double newAngle) {
            backgroundImage.setAngle(newAngle);
            Label<Button>::setAngle(newAngle);
            return *this;
        }
    
        Label<Button> backgroundImage;
    };
    
    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(0.0); 
    }
    
    0 讨论(0)
提交回复
热议问题