父返回子对象中的C ++函数

问题描述 投票:5回答:3

我想在我的班级中使用更改器(设置器)以返回this以允许类似jQuery的a.name("something").address("somethingelse");我有一个父类(Entity)和几个子类(Client, Agent etc.)。大多数事物的转换器都继承自Entity类(例如名称或地址),但是它们返回Entity对象,因此我无法在其上调用客户端转换器。

换句话说:

// 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("UserName123").budgetRange(50);

编译器(从逻辑上说,该Entity对象没有budgetRange成员(因为名称返回一个Entity,而不是Client)。

我现在的问题是:我怎样才能实现这样的目标?我曾考虑过重载子类中的所有Entity函数,但这不是很好,并且违背了继承的概念:)

预先感谢您的想法:D

c++ inheritance parent-child setter mutators
3个回答
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
{...};

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继承。

现在几乎所有内容都已经讲完了,我想添加一个答案,允许一个人在多个继承级别上使用CRTP:

上述CRTP实现在想要从Client继承时会中断,因为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> {};

这是否值得扩展代码库完全取决于您。注意,我还没有编译上面的代码。


2
投票

现在几乎所有内容都已经讲完了,我想添加一个答案,允许一个人在多个继承级别上使用CRTP:

© www.soinside.com 2019 - 2024. All rights reserved.