C++中的流畅接口和继承

9

我希望创建一个基类(称之为type::base),它具有一些常见的功能和流畅的接口,但问题是所有这些方法的返回类型。

  class base {
    public:
       base();
       virtual ~base();

       base& with_foo();
       base& with_bar();
    protected:
       // whatever...
  };

现在我可以创建子类型,例如:
  class my_type : public base {
    public:
      myType();        
      // more methods...
  };

问题出现在使用这些子类型时,例如:
 my_type build_my_type()
 {
    return my_type().with_foo().with_bar();
 }

这段代码不能编译,因为我们返回了base而不是my_type。

我知道我可以这样做:

 my_type build_my_type()
 {
    my_type ret;
    ret.with_foo().with_bar();

    return ret;
 }

但我在思考如何实现它,却没有找到任何有效的想法,有什么建议吗?


我已经删除了命名空间的内容,因为据我所见,它与问题无关。 - anon
5个回答

4
这个“类型丢失”的问题可以通过模板来解决,但这相当复杂。
例如:
class Pizza
{
  string topping;
public:
  virtual double price() const;
};

template <class T, class Base>
class FluentPizza : public Base
{
  T* withAnchovies() { ... some implementation ... };
};

class RectPizza : public FluentPizza<RectPizza, Pizza>
{
  double price() const { return length*width; :) }
};

class SquarePizza : public FluentPizza<SquarePizza, RectPizza>
{
   ... something else ...
};

您可以编写以下内容:
SquarePizza* p=(new SquarePizza)->withAnchovies();

模式是,而不是
class T : public B

you write

class T : public Fluent<T, B>

另一种方法是不在对象上使用流畅接口,而是在指针上使用流畅接口:
class Pizza { ... };
class RectPizza { ... };
class SquarePizza { ... whatever you might imagine ... };

template <class T>
class FluentPizzaPtr
{
  T* pizza;
public:
  FluentPizzaPtr withAnchovies() {
    pizza->addAnchovies(); // a nonfluent method
    return *this;
  }
};

使用方法如下:

FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... }

FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies();

你能提供一个例子吗?这可能会很有趣。 - liori
参考编辑。这可能很有趣,但我不确定我个人会使用它... - jpalecek
所以你实际上切换到指针。然后,您可以使用简单的多态性,而无需使用CRTP,并返回base*。 - liori
你可以将引用想象为(如果你不想使用指针)。我只是不会让方法总是返回一份副本,但这并不是核心问题。CRTP 的主要目的是消除基类衰减(或基类指针、基类引用),这正是我认为你想避免的。 - jpalecek
1
如果你要返回指针或引用,请返回static_cast<RectPizza*>(this)或static_cast<RectPizza&>(this)(如果您不介意更改原始内容,后者也适用于副本)。如果您想返回一个副本并保留原始内容不变,请执行类似“T ret=(T*)this; do_something_with_ret; return ret;”的操作。 - jpalecek
显示剩余2条评论

4
你应该返回引用/指针,而不需要保留类型信息。
class base {
  public:
     base();
     virtual ~base();

     base &with_foo();
     base &with_bar();
  protected:
     // whatever...
};

class my_type : public base {
  public:
    my_type();        
    // more methods...
};

base *build_my_type()
{
   return &new my_type()->with_foo().with_bar();
}

您已经有一个虚析构函数了。假设您还有其他虚函数。通过基类型和在那里声明的虚函数访问所有内容。


1
问题在于我不想丢失类型。 - blaxter

0
一个解决方案可以这样实现:
return *static_cast<my_type*>(&my_type().with_foo().with_bar());

使用 static_cast 基本上告诉编译器“我知道我在这里做什么”。


-1
在C++中,你应该返回指针或引用而不是值。此外,你可能想解释一下你所说的“流畅接口”的含义。

1
抱歉 - 只要我看到“Martin”这个名字,我的废话检测器就会以最大音量响起,我就无法继续阅读了。似乎无论是名字还是姓氏都一样。 - anon

-2
我会在C#中这样做,我相信在C++中也可以实现,就是为with_foo()with_bar()提供一个默认的实现...请原谅我的C#:

class base {
  virtual base with_foo()
  { throw new NotImplementedException(); }
  virtual base with_bar();
  { throw new NotImplementedException(); }
}

但这并不能解决问题 - 问题是类型不匹配,与问题无关。 - jpalecek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接