是否可以编写返回派生类型的流畅的通道方法?考虑以下两个类:
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
类包含方法withParam
和doPost
,另一个GettableRest
类包含doGet
}}。我怀疑这是不可能的,并且可能会尝试将一堆虚拟方法塞进RestConnection
但我讨厌在多个withParam
被重载时这样做,其中一些没有意义包含在GET参数列表中。
提前致谢!
答案 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是具体类型 - 那么您将需要引入一个新的抽象类,它将是Base
和Derived
的基本类型。
这种方式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),并且它的缺点是它基于一个功能(协变返回类型的虚函数)并不是所有编译器都支持。