如何在C++模板子类中部分重载虚拟函数



我正在尝试创建一个非模板基类来创建一个接口,通过该接口我可以与派生的模板类进行交互。我希望使用部分虚拟函数重载,但有些东西不起作用,我不知道为什么。有人能解释为什么我的以下代码返回:

B match = False
D match = True
E match = False

而不是B,D返回True,E返回False?我本以为派生类中的重载运算符会拾取指向int‘&我会被召唤,但没有。

需要明确的是,我并没有试图覆盖match的Base版本,我正在尝试重载它,特别是希望它有一个与Base中的接口不同的,在这种情况下更专业的接口,后者在使用其函数签名时接管它。

我还试图避免为我可能实例化的派生模板的每种风格扩展基类。

即使是陌生人,我——可能——只是在这里疯了,但我发誓这在不久前的某个时候对我有用!现在我在OSX10.5上,使用llvm 7.0.0和clang 700.1.76。这可能是编译器的特殊性吗?

虽然我试图在这里使用部分重载(但没有成功),但我对任何能够解决根据参数类型选择模板实例函数的问题的方法都持开放态度,而不需要激增类、if-thens/case或向基类添加特定专业化。如果你有其他方法,我很想听听,我可以顺便来看看类似的功能。

感谢您提供的任何见解!

#include <stdio.h>
class Base
{
public:
  Base() {}
  virtual ~Base(){}
  virtual bool match( const void *data ) const { return false; }
};
template <class Type>
class Derived: public Base
{
public:
  Derived():Base() {}
  ~Derived() override{}
  virtual bool match( const Type *data ) const { return true; }
};
int main(int argc, char **argv)
{
  Derived<int>   *d = new Derived<int>();
  Derived<float> *e = new Derived<float>();
  Base *b           = d;
  int i;
  printf("B match = %sn",b->match(&i)?"True":"False");
  printf("D match = %sn",d->match(&i)?"True":"False");
  printf("E match = %sn",e->match(&i)?"True":"False");
}

如果手动将Derived<int>创建为类,则其成员函数为:

virtual bool match( const int *data ) const { return true; }

它不会覆盖基类。因此,当您用基类指针调用函数时,它会执行基类实现,而当您用派生类指针调用它时,它则会执行派生类实现。

如果使用override关键字,您将能够在编译时发现问题。

template <class Type>
class Derived: public Base
{
public:
  Derived():Base() {}
  ~Derived() override{}
  virtual bool match( const Type *data ) const override { return true; }
};

您应该看到该更改的编译时错误。

请参阅上的编译器错误http://ideone.com/8rBQ6B.

更新,以回应OP的意见

如果你不想覆盖:

  1. 不要在成员函数声明中使用virtual
  2. 使用将基类函数带入派生类的范围

    using Base::match
    

    以下是操作方法:

    template <class Type>
    class Derived: public Base
    {
    public:
      Derived():Base() {}
      ~Derived() override{}
      using Base::match;
      bool match( const Type *data ) const { return true; }
    };
    

这里的问题是Derived::matchBase::match不匹配。虚拟函数的函数签名必须相同,才能使机制工作。因此,使用您的代码Derived::match重载而不是覆盖它。

如果我们更改

virtual bool match( const Type *data ) const { return true; }

virtual bool match( const void *data ) const { return true; }

然后我们得到

B match = True
D match = True

Live示例

因为函数的签名不匹配:

class Base中,您有

virtual bool match( const void *data ) const { return false; }

注意const void *参数类型

那么在Derived中你有

virtual bool match( const Type *data ) const { return true; }

其中,本例中的Typeint(来自mainDerived<int> *d

只需在Base中添加一个带有const int* data签名的虚拟,就可以开始了。

是的,这确实意味着在Base中,您必须为每个期望与Derived一起使用的Type添加match的过载。

这不起作用的原因是派生类实际上并没有覆盖基类中的虚拟函数。为了覆盖基类中的虚拟函数,派生类必须具有完全相同的签名(有关异常,请参阅下面的注释)。在这种情况下,签名是不同的,因为基类中的虚拟函数采用void *,而派生类中的函数则采用type *

让我们做一个小的更改,并将override指令添加到派生类中的函数签名:

#include <stdio.h>
class Base
{
public:
  Base() {}
  virtual ~Base(){}
  virtual bool match( const void *data ) const { return false; }
};
template <class Type>
class Derived: public Base
{
public:
  Derived():Base() {}
  ~Derived() override{}
  virtual bool match( const Type *data ) const override { return true; }
};
int main(int argc, char **argv)
{
  Derived<int> *d = new Derived<int>();
  Base *b         = d;
  int i;
  printf("B match = %sn",b->match(&i)?"True":"False");
  printf("D match = %sn",d->match(&i)?"True":"False");
}

以下是我们现在编译时发生的情况:

main.cpp: In instantiation of 'class Derived<int>':
main.cpp:24:38:   required from here
main.cpp:19:16: error: 'bool Derived<Type>::match(const Type*) const [with Type = int]' marked 'override', but does not override
   virtual bool match( const Type *data ) const override { return true; }

假设我们使用的是C++11,那么使用override来确保我们真的在重写基类虚拟函数总是一个好主意。

注意:有一种叫做协变返回类型的东西,其中重写不必具有相同的签名,但这超出了问题的范围。

感谢o_weisman在原始问题下的评论,我能够使用函数模板找到一个有效的解决方案(见下文)。诚然,在我的解决方案的当前形式中,我利用了一个限制,即Derived的每个Type实例都是一个singleton。这适用于我的特定设计,但行为可以根据需要进行扩展。允许Derived的多个实例的一种可能性是,检查Base::match中的"this"指针是否在所有实例的集合中(在构造/析构函数时更新的静态集合变量中跟踪),而不是针对单个单例实例。无论如何,我希望这能帮助那些可能面临类似设计挑战的人。

#include <stdio.h>
#include <assert.h>
template <class Type> class Derived;
class Base
{
public:
  Base() {}
  virtual ~Base(){}
  template <class Type>
  bool match( const Type *data ) const { return (Derived<Type>::instance() == this); }
};
template <class Type>
class Derived: public Base
{
public:
  Derived(): Base() { assert(!ourInstance); ourInstance = this; }
  ~Derived() override{}
  static const Base *instance() { return ourInstance; }
protected:
  static const Derived<Type> *ourInstance;
};
template <class Type>
const Derived<Type> *Derived<Type>::ourInstance = NULL;
int main(int argc, char **argv)
{
  Derived<int>   *d = new Derived<int>();
  Derived<float> *e = new Derived<float>();
  Base *b           = d;
  int i;
  printf("B match = %sn",b->match(&i)?"True":"False");
  printf("D match = %sn",d->match(&i)?"True":"False");
  printf("E match = %sn",e->match(&i)?"True":"False");
}

这产生了所需的结果:

B match = True
D match = True
E match = False

最新更新