2011-12-20 65 views
5

说实话,我真的不知道,怎么问这个问题,所以请别生气:)C++在父母子女回报功能

无论如何,我希望有存取器(setter方法)在我的课返回this允许的jQuery样a.name("something").address("somethingelse"); 我有一个父类(Entity)和几个childclasses(Client, Agent etc.)。对于大多数事情来说,mutators是从Entity类(如名称或地址)继承而来的,但它们返回一个Entity对象,所以我无法在它们上调用Client增强器。

换句话说:

// 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); 

编译器(逻辑)说,该实体对象没有budgetRange成员(因为名字返回一个实体,不是客户)。

我现在的问题是:如何可以实现这样的事情?我想到了在childclasses超载所有的实体功能,但是这不会是好的,是对继承:)

预先感谢您为您的想法的想法:d

+0

谷歌这是你在做什么。请注意,从'Client'继承时,您的代码将在下面使用CRTP解决方案再次中断。 – cheind 2011-12-20 13:40:05

回答

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 
{...}; 
+0

我真的很喜欢这个想法(特别是This()函数^^)。 非常感谢,这就是我一直在寻找:) – Asmodiel 2011-12-20 13:22:31

+0

优秀的东西。我一直在寻找一个CRTP的好例子。谢谢。 – sje397 2011-12-20 13:26:40

+0

@ sje397 CRTP的另一个好例子是一个通用的树类,它通过'template class node'提供了具体的节点实现。例如:'D&node :: left()'可以提供具体节点impl。这样你就可以将所有的树逻辑填充到通用节点类中。 – cheind 2011-12-20 13:46:06

3

的“好奇递归模板“模式可以在这里帮助;使基类的模板,通过派生类parametrised,沿着线:

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继承这只会工作。如果你需要类型是多态的(即所有类型都共享一个基类),那么你需要添加另一个非模板基类,并且从中继承。

+0

非常感谢您的回答,但是我会选择Abyx'one,因为他包含了这个()的想法:) Upvoted。 – Asmodiel 2011-12-20 13:25:08

2

现在,几乎所有已经说过,我要加一块答案允许一个人在继承的多层次使用CRTP:

当一个人想从Client继承,因为Derived将把Client以上CRTP实现突破。如果你希望能够使用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> {}; 

这是否值得扩大的代码库完全取决于你。请注意,我没有编译上面的代码。也为`方法chaining`和`命名参数idiom`,