具有多态性 C++ 的方法链接

method chaining with polymorphism c++

本文关键字:方法 链接 C++ 多态性      更新时间:2023-10-16

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

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 服务器。 我有一个带有方法 withUrlRestConnection 类,一个带有方法 withParamdoPostPostableRest 类,以及一个带有 doGetGettableRest类。 我怀疑这是不可能的,并且可能会尝试将一堆虚拟方法塞进RestConnection但是当有多个withParam过载时,我讨厌这样做,其中一些方法对于包含在GET参数列表中没有意义。

提前感谢!

我认为你可以在这里使用 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;
    }
};

看看奇怪的重复出现的模板模式。

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

这样withFoo就可以模板化以返回实际类型。

您的选项要么是 CRTP(如标记 B 所示(,要么对变量名使用运行时调度,例如。

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

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

由于你的类型不是多态的(没有虚函数(,所以 Base 没有派生的知识。

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

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&or 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(,并且缺点是它基于并非所有编译器都支持的功能(虚函数的协变返回类型(。