老实说,我真的不知道,怎么问这个问题,所以请不要生气:)
无论如何,我希望在我的班级中使用mutators(setter)来返回this
以允许类似jQuery的a.name("something").address("somethingelse");
我有一个父类(Entity
)和几个子类(Client, Agent etc.
)。大多数事物的mutator都是从Entity
类继承的(比如名称或地址),但它们返回一个Entity
对象,所以我不能在它们上调用Client mutators。
换句话说:
// name mutator
Entity& Entity::name( const string& name ) {
// [...] checks
_name = name;
return *this;
}
// budgetRange mutator
Client& Client::budgetRange( const long int& range ) {
// [...] checks
_budgetRange = range;
return *this;
}
然后我打电话给它:
Client a; a.name("Dorota Adamczyk").budgetRange(50);
编译器(逻辑上)说,Entity对象没有budgetRange成员(因为name返回一个Entity而不是Client)。
我现在的问题是:我怎么能实现这样的东西?我考虑过重载子类中的所有实体函数,但这不会很好,并且会违反继承的想法:)
提前感谢您的想法:D
答案 0 :(得分:7)
您应该使用CRTP。
template<class Derived>
class Entity
{
Derived* This() { return static_cast<Derived*>(this); }
public:
Derived& name(const string& name)
{
...
return *This();
}
};
class Client : public Entity<Client>
{
public:
Client& budgetRange(const long& range)
{
...
return *this;
}
};
如果要使用虚函数,还可以添加抽象基类,如下所示:
class AbstractEntity
{
public:
virtual void foo() = 0;
virtual ~AbstractEntity();
};
template<class Derived>
class Entity : AbstractEntity
{...};
答案 1 :(得分:3)
“奇怪的递归模板”模式可以在这里提供帮助;使基类成为模板,由派生类进行参数化,如下所示:
template <typename Derived>
struct Entity {
Derived & name(std::string const & name) {
// stuff
return static_cast<Derived&>(*this);
}
};
struct Client : Entity<Client> {
Client & budget(long range) {
// stuff
return *this;
}
};
Client().name("Mike").budget(50); // should compile
仅当所有类型都直接从Entity
继承时,此功能才有效。如果您需要多态类型(即所有类型共享一个公共基类),那么您将需要添加另一个非模板基类,并从中继承Entity
。
答案 2 :(得分:2)
现在几乎所有内容都已经说过,我想添加一个答案,允许人们在多个继承级别上使用CRTP:
当想要从Client
继承时,上述CRTP实现会中断,因为Derived
将引用Client
。如果您希望能够使用CRTP模式在多个继承级别上携带命名参数习惯用法,则需要像这样对类进行编码
template<class Derived>
class Entity_T
{
protected:
Derived* This() { return static_cast<Derived*>(this); }
public:
Derived& name(const string& name)
{
...
return *This();
}
};
template<class Derived>
class Client_T : public Entity_T<Derived>
{
Derived& budgetRange(const long& range)
{
...
return *This();
}
};
为用户提供Client_T
添加
class Client : public Client_T<Client> {};
这是否值得扩大代码库完全取决于您。请注意,我还没有编译上面的代码。