繁体   English   中英

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

[英]How can I partially overload a virtual function in a C++ template subclass?

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

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

而不是B,D返回True,E返回False? 我原以为派生类中的重载运算符会拾取指向int'&i'并被调用,但事实并非如此。

为了清楚起见,我不是试图 - 覆盖 - 匹配的基本版本,我正在尝试-overload-它,特别希望它有一个不同的,在这种情况下比Base中的那个更专业的接口,使用其功能签名时接管。

我也试图避免为我可能实例化的Derived模板的每种风格扩展Base类。

更奇怪的是,我 - 可能 - 只是在这里疯了,但我发誓这在不久前的某个时刻对我有用! Fwiw我在OSX 10.5上,使用llvm 7.0.0和clang 700.1.76。 这可能是编译器的特性吗?

虽然我在这里尝试(不成功)使用部分重载,但我真的愿意采用任何方法来解决通过其参数类型选择模板实例函数的问题,而不会增加类,if-thens / case或者添加特定的Base类的特化。 如果您有其他方法可以用于类似功能,我很乐意听到它。

感谢您提供的任何见解!

#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 = %s\n",b->match(&i)?"True":"False");
  printf("D match = %s\n",d->match(&i)?"True":"False");
  printf("E match = %s\n",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::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

实例

因为函数的签名不匹配:

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 = %s\n",b->match(&i)?"True":"False");
  printf("D match = %s\n",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-in​​stance都是一个单例。 这适用于我的特定设计,但可以根据需要扩展行为。 允许多个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 = %s\n",b->match(&i)?"True":"False");
  printf("D match = %s\n",d->match(&i)?"True":"False");
  printf("E match = %s\n",e->match(&i)?"True":"False");

}

这产生了以下期望的结果:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM