【问题标题】:C++ function in parent return child父返回子中的 C++ 函数
【发布时间】:2011-12-20 13:08:36
【问题描述】:

我希望我的班级中的 mutators (setter) 返回 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 成员(因为name 返回的是Entity,而不是Client)。

我现在的问题是:我怎样才能实现这样的东西?我考虑过重载子类中的所有实体函数,但这并不好,而且会违背继承的想法:)

提前感谢您的想法:D

【问题讨论】:

  • google 也为method chainingnamed parameter idiom,这就是你正在做的。请注意,当从 Client 继承时,您的代码将使用下面的 CRTP 解决方案再次中断。

标签: c++ inheritance parent-child setter mutators


【解决方案1】:

您应该使用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
{...};

【讨论】:

  • 我真的很喜欢这个想法(尤其是 This() 函数^^)。非常感谢,这就是我想要的:)
  • 好东西。我一直在寻找 CRTP 的一个很好的例子。谢谢。
  • @sje397 CRTP 的另一个很好的例子是一个通用的树类,它通过template&lt;class D&gt; class node 提供具体的节点实现。例如:D &amp;node::left() 可以提供具体的节点impl。这样您就可以将所有树逻辑填充到通用节点类中。
  • @Christoph Heindl:另一个很好的例子。谢谢。
  • 请注意,您的类现在不再具有多态基类。它们完全相互独立。现在每个派生类都有一个不同的基类。
【解决方案2】:

“奇怪的递归模板”模式可以在这里提供帮助;使基类成为模板,由派生类参数化,遵循以下原则:

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

【讨论】:

  • 非常感谢您的回答,但我会选择 Abyx 的,因为他包含了 this() 的想法 :) 支持。
【解决方案3】:

现在几乎所有内容都已经说了,我想添加一条答案,允许人们在多个继承级别上使用 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_Tadd的无模板版本

class Client : public Client_T<Client> {};

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

【讨论】:

    猜你喜欢
    • 2020-07-13
    • 1970-01-01
    • 2021-10-02
    • 2015-02-13
    • 1970-01-01
    • 2022-11-17
    • 2010-10-01
    • 2015-10-31
    • 1970-01-01
    相关资源
    最近更新 更多