通过模板的 C++ 混合:为啥这不起作用?

Posted

技术标签:

【中文标题】通过模板的 C++ 混合:为啥这不起作用?【英文标题】:C++ mixins via templates: why doesn't this work?通过模板的 C++ 混合:为什么这不起作用? 【发布时间】:2010-06-22 11:06:30 【问题描述】:

我有一个接口,它被实现为一个抽象基类,带有许多纯虚拟公共方法。这些纯虚函数可以使用模板来实现,因为子类之间的差异不大——所以我的想法是使用多重继承来混合提供实现的适当模板化的帮助类。但是,编译器抱怨基类是抽象的;它没有考虑 helper mix-in 的实现,所以认为 no 实现了所需的方法。

例如:

class TrivialList 
    int count;
public:
    TrivialList(int count) : count(count)
    virtual double Average() const=0;
    int Count() const return count;
    virtual ~TrivialList()
;
template<typename TIndexable> class AverageHelper 
public:
    double Average() const 
        TIndexable const & self = static_cast<TIndexable const &>(*this);
        double sum=0.0;
        for(int i=0;i<self.Count();++) sum += self.Get(i);
        return sum / self.Count();
    
;
class IndexableList : public TrivialList, public AverageHelper<IndexableList> 
    std::vector<double> backend;
public:
    IndexableList(int count) : TrivialList(count), backend(count)  
    double & Get(int i)  return backend[i];
    double const & Get(int i) const  return backend[i];
;
IndexableList * MakeList() return new IndexableList(5); //error!
//    cannot instantiate abstract class

我正在使用 MSC 10.0 (Visual Studio 2010);使用 g++ 4.5 时代码失败并出现类似错误。

Get 或我项目中的现实世界等价物不能是虚拟的,因为它们是非常小的操作,需要内联以获得足够的性能(想想 put-pixel/get-pixel) - 所以我需要通用算法通过虚函数调用进行模板化而不是泛型化。

【问题讨论】:

【参考方案1】:

要通过模板实现混入,您需要实现抽象函数的模板派生自抽象基类。

因此您可以通过以下方式更改代码来修复您的代码:

// ...
template<typename TIndexable> class AverageHelper : public TriviaList

// ...
class IndexableList : public AverageHelper<IndexableList> 

一般来说,如果您想提供多个混入,您可以使用虚拟继承以不增加基类的实例,或者使用链式继承,如下例所示:

class Abstract 
public:
    virtual void foo() = 0;
    virtual void bar() = 0;
;

template<class Base>
class FooImpl : Base 
public:
    void foo()  /* default foo implementation */ 
;

template<class Base>
class BarImpl : Base 
public:
    void bar()  /* default bar implementation */ 
;

class Derived : public BarImpl<FooImpl<Abstract> > 
    // You have both foo() and bar() implementations available
;

【讨论】:

我需要多个 mixin - 将它们链接起来听起来很棘手;这将如何与虚拟继承一起工作? 如果我使用链接,我该如何处理构造函数参数? 虚拟继承的工作方式非常简单(你只是虚拟地从基类派生每个 mixin,并从 mixins 派生真正的实现),但它会引起关于支配性的警告,但我通常使用链式继承,离开用于实现多个不同抽象接口的多重继承。 +1:感谢您的想法; sbi 的回答更适合我的需要,所以我会将他标记为答案。【参考方案2】:

它不起作用,因为AverageHelper&lt;&gt;::Average() 不会覆盖TrivialList::Average()。为了重写虚函数,重写类必须从包含要重写的函数的类继承。

你可以这样改变你的模板:

template<typename TIndexable, typename Base > 
class AverageHelper : public Base 
public:
  template< typename T >
  AverageHelper(T arg) : Base(arg) 
  // ... 
;

class IndexableList : public AverageHelper<IndexableList,TrivialList> 
public:
  IndexableList(int count) : AverageHelper<IndexableList,TrivialList>(count) 
  // ...
;

您可能希望从TrivialList 虚拟派生:

template<typename TIndexable, typename Base > 
class AverageHelper : virtual public Base 
  // ... 
;

【讨论】:

虚拟继承有缺点吗? @Eamon:访问基类子对象的效率较低。我猜对于常见的实现,这是一种额外的间接方式。但是,在 MI 场景中,这通常是不可避免的。但是,有时您想要为多重继承的基类创建单独的子对象。我想未能提供这些可能被视为虚拟继承的缺点。 :) Base 继承AverageHelper 无法正确调用Base 的构造函数。在我的示例中,构造函数可能会被删除,但总的来说,保留非默认构造函数的选项会很好。 感谢您的帮助!我倾向于摆脱构造函数,只使用显式的Init 方法来解决这个问题。 @Eamon:抽象基类最好没有数据。没有数据,它们通常不需要构造函数。抽象类通常也是虚拟基类,而虚拟基类无论如何都应该有默认构造函数。

以上是关于通过模板的 C++ 混合:为啥这不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 cvWaitKey(0) 不起作用?

为啥 subscribe() 不起作用但模板中的异步可以?

(这里是极端菜鸟)为啥这个 C 代码不起作用?

为啥使用命名空间在我的 C++ 项目中不起作用?

为啥 WCF 休息服务(不是使用 WCF 休息服务模板创建的)不起作用?

如何做一个简单的缩放动画,为啥这不起作用?