方法链接多态c ++

时间:2013-08-22 15:25:25

标签: c++ polymorphism fluent

是否可以编写返回派生类型的流畅的通道方法?考虑以下两个类:

class Base {
protected:
    std::string mFoo;
public:
    Base& withFoo(std::string foo) {
        mFoo = foo;
        return *this;
    }
};

class Derived : public Base {
protected:
    std::string mBar;
public:
    Derived& withBar(std::string bar) {
        mBar = bar;
        return *this;
    }

    void doOutput() {
        std::cout << "Foo is " <<
            mFoo << ".  Bar is " <<
            mBar << "." << std::endl;
    }
};

然后我想构建我的对象并像这样使用它:

Derived d;
d.withFoo("foo").withBar("bar").doOutput();

由于withFoo返回Base,因此当然失败了。由于我的所有with方法都只是设置成员变量,因此我可以先指定派生的with。问题是我的构建器方法(上例中的doOutput)需要是一个单独的语句。

Derived d;
d.withBar("this is a bar")
    .withFoo("this is my foo");
d.doOutput();

我的问题是withFoo是否有某种方法可以返回未知的派生类型,以便Base可以与多个派生类无缝地使用(毕竟,*this Derived,虽然Base(正确)不知道这一事实。)

对于更具体的示例,我正在编写一些类来访问REST服务器。我有一个RestConnection类,方法为withUrl,一个PostableRest类包含方法withParamdoPost,另一个GettableRest类包含doGet }}。我怀疑这是不可能的,并且可能会尝试将一堆虚拟方法塞进RestConnection但我讨厌在多个withParam被重载时这样做,其中一些没有意义包含在GET参数列表中。

提前致谢!

4 个答案:

答案 0 :(得分:3)

我认为你可以在这里使用CRTP,如下所示,派生类告诉基数它是什么类型:

class Base
{
    // Abstract/virtual interface here.
};

template <class Derived>
class Base_T : public Base
{
private:
    std::string mFoo;

public:
    Derived& withFoo(std::string foo) {
        mFoo = foo;
        return *static_cast<Derived*>(this);
    }
};

class Derived : public Base_T<Derived> {
private:
    std::string mBar;
public:
    Derived& withBar(std::string bar) {
        mBar = bar;
        return *this;
    }

    void doOutput() {
        std::cout << "Foo is " <<
            mFoo << ".  Bar is " <<
            mBar << "." << std::endl;
    }
};

答案 1 :(得分:0)

看看Curiously recurring template pattern

如果Base是抽象类型(仅在其子类中实例化),则将其设为采用类型名称的模板。您的Derive会扩展模板 - 例如Derived : public Base<Derived>。如果Base是具体类型 - 那么您将需要引入一个新的抽象类,它将是BaseDerived的基本类型。

这种方式withFoo可以模板化以返回实际类型。

答案 2 :(得分:0)

您可以选择CRTP(如图示的Mark B),也可以使用变量名称的运行时调度,例如。

Derived d;
d.with("Foo", "foo").with("Bar", "bar").doOutput();

这不会特别高效,但它非常灵活,并且可以接受任意字段的协议。

答案 3 :(得分:0)

由于您的类型不是多态的(没有虚函数)Base没有Derived的知识。

您可以通过静态多态来实现客观的目标:

template<class Derived>
class Base {
protected:
    std::string mFoo;
public:
    Derived& withFoo(std::string foo) {
        mFoo = foo;
        return static_cast<Derived&>(*this);
    }
};

class Derived : public Base<Derived> {
protected:
 ......
}

缺点是不再存在Base类,但可能导出的Base实例数量很多,因此你不能再拥有一个基数&amp;或Base *指向Derived。

如果你需要一个共同基础来收集,你需要另一个CommonBase(不是寺庙化的),Base可以从中派生出来。

另一种可能性是通过使withFoo虚拟来使Base(旧的)变形。 此时,在Derived中,您可以覆盖withFoo以返回Derived&amp;协变类型:

class Base
{
   ...
   virtual Base& withFoo(...);
   ...
   virtual ~Base() {} //just to make all the hierarchy destructible through base
};

class Derived: public Base
{
   ...
   virtual Derived& withFoo(type arg)
   { return static_cast<Derived&>(Base::withFoo(arg)); }
   ...
};

这仍然包含了经典的OOP范例,但增加了运行时开销(vtable),并且它的缺点是它基于一个功能(协变返回类型的虚函数)并不是所有编译器都支持。