1

是否可以编写返回派生类型的流利的链接方法?考虑以下两个类:

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先指定派生的 s。问题是我的构建器方法(doOutput在上面的示例中)需要单独声明。

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

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

举一个更具体的例子,我正在编写一些类来访问 REST 服务器。我有一个RestConnection带有方法的类withUrl,一个PostableRest带有方法withParam和的类doPost,以及一个GettableRest带有doGet. 我怀疑这是不可能的,并且可能会尝试将一堆虚拟方法塞入RestConnection其中,但是当有多个withParams 重载时,我讨厌这样做,其中一些包含在 GET 参数列表中没有意义。

提前致谢!

4

4 回答 4

4

我认为您可以在这里使用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;
    }
};
于 2013-08-22T15:43:24.530 回答
0

看看Curiously recurring template pattern

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

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

于 2013-08-22T15:43:05.763 回答
0

您的选项要么是 CRTP(如 Mark B 所示),要么是在变量名上使用运行时调度,例如。

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

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

于 2013-08-22T15:46:56.720 回答
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的实例化可能派生,因此你不能再有一个 Base& 或 Base* 指向任何派生的。

如果您需要一个通用基础来收集,您需要另一个通用基础(未模板化),基础可以从中派生。

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

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 范例,但增加了运行时开销(vtables),并且缺点是它基于并非所有编译器都支持的特性(虚函数的协变返回类型)。

于 2013-08-22T15:47:40.200 回答