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

时间:2015-11-06 20:38:10

标签: c++ templates overloading

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

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

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

要说清楚,我不是试图 - 覆盖 - 匹配的Base版本,我正在尝试-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");

}

5 个答案:

答案 0 :(得分:3)

如果要手动创建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; }
    };
    

答案 1 :(得分:1)

此处的问题是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 Example

答案 2 :(得分:1)

因为函数的签名不匹配:

class Base你有

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

请注意const void *参数类型

然后在Derived你有

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

此实例中的Typeint(来自main的{​​{1}})

只需在Base中添加一个带有Derived<int> *d签名的虚拟广告,就可以了。

是的,这确实意味着在const int* data中,您必须为Base的每个match添加Type的重载。

答案 3 :(得分:1)

这不起作用的原因是因为派生类实际上覆盖基类中的虚函数。为了覆盖基类中的虚函数,派生类必须具有完全相同的签名(请参阅下面的注释以了解异常)。在这种情况下,签名是不同的,因为基类中的虚函数采用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来确保我们真的覆盖基类虚函数。

从上面注意:有一个名为covariant return type的内容,其中覆盖不必具有相同的签名,但这超出了问题的范围。

答案 4 :(得分:1)

感谢 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