简体   繁体   English

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

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

I'm trying to create a non-template base class to create an interface through which I can interact with derived template classes. 我正在尝试创建一个非模板基类来创建一个接口,通过该接口我可以与派生的模板类进行交互。 My hope is to use partial virtual function overloads, but something isn't working, and I'm not sure why. 我希望使用部分虚函数重载,但有些东西不起作用,我不知道为什么。 Can anyone explain why I the below code returns: 任何人都可以解释为什么我下面的代码返回:

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

instead of B,D returning True, and E returning False? 而不是B,D返回True,E返回False? I would have thought that the overloaded operator in the derived class would pick up the pointer-to-int '&i' and be called, but it doesn't. 我原以为派生类中的重载运算符会拾取指向int'&i'并被调用,但事实并非如此。

To be clear, I'm not trying to -override- the Base version of match, I am trying to -overload- it, specifically wanting it to have a different, in this case more specialized, interface than the one in Base, which takes over when its function signature is used. 为了清楚起见,我不是试图 - 覆盖 - 匹配的基本版本,我正在尝试-overload-它,特别希望它有一个不同的,在这种情况下比Base中的那个更专业的接口,使用其功能签名时接管。

I'm also trying to avoid having to extend the Base class for every flavor of the Derived template that I might instantiate. 我也试图避免为我可能实例化的Derived模板的每种风格扩展Base类。

Even stranger, and I -might- just be going crazy here, but I swear this was working for me at some point not too long ago! 更奇怪的是,我 - 可能 - 只是在这里疯了,但我发誓这在不久前的某个时刻对我有用! Fwiw I'm on OSX 10.5, using llvm 7.0.0 and clang 700.1.76. Fwiw我在OSX 10.5上,使用llvm 7.0.0和clang 700.1.76。 Could this be a compiler idiosyncracy? 这可能是编译器的特性吗?

While I am attempting (unsuccessfully) to use partial overloads here, I'm really open to any approach that solves the problem of picking a template instance function by the type of its arguments, without proliferating classes, if-thens/case or adding specific specializations to the Base class. 虽然我在这里尝试(不成功)使用部分重载,但我真的愿意采用任何方法来解决通过其参数类型选择模板实例函数的问题,而不会增加类,if-thens / case或者添加特定的Base类的特化。 I'd love to hear it if you have another approach that I could drop in for similar functionality. 如果您有其他方法可以用于类似功能,我很乐意听到它。

Thank you for any insights you can offer! 感谢您提供的任何见解!

#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");

}

If you were to manually create Derived<int> as a class, its member function would be: 如果您要手动创建Derived<int>作为类,其成员函数将是:

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

It doesn't override the base class. 它不会覆盖基类。 Hence, when you invoke the function with base class pointer, it executes the base class implementation and when you invoke it with the derived class pointer, it executes the derived class implementation. 因此,当您使用基类指针调用该函数时,它会执行基类实现,当您使用派生类指针调用它时,它会执行派生类实现。

You will be able to catch the problem at compile time if you use the override keyword. 如果使用override关键字,则可以在编译时捕获问题。

template <class Type>
class Derived: public Base
{
public:
  Derived():Base() {}
  ~Derived() override{}

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

You should see a compile time error with that change. 您应该看到该更改的编译时错误。

See the compiler error at http://ideone.com/8rBQ6B . 请参阅http://ideone.com/8rBQ6B上的编译器错误。

Update, in response to OP's comment 更新,以回应OP的评论

If you don't mean to override: 如果你不是要覆盖:

  1. Don't use virtual in the member function declaration. 不要在成员函数声明中使用virtual
  2. Bring the base class function into the scope of the derived class by using 通过使用将基类函数放入派生类的范围

     using Base::match 

    Here's how to do it: 这是怎么做的:

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

The problem here is that Derived::match does not match Base::match . 这里的问题是Derived::matchBase::matchBase::match The function signatures of virtual functions have to be the same for the mechanism to work. 虚函数的函数签名必须与工作机制相同。 So with your code Derived::match overloads instead of overriding it. 因此,使用您的代码Derived::match重载而不是覆盖它。

If we change 如果我们改变

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

To

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

Then we get 然后我们得到

B match = True
D match = True

Live Example 实例

because the signatures of the functions do not match: 因为函数的签名不匹配:

In class Base you have class Base你有

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

note the const void * parameter type 请注意const void *参数类型

then in Derived you have 然后在Derived你有

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

Where Type in this instance is int (from main 's Derived<int> *d ) 此实例中的Typeint (来自mainDerived<int> *d

Just add a virtual in Base with a const int* data signature, then you'll be good to go. 只需在Base中添加一个带有const int* data签名的虚拟,然后就可以了。

And yes, that does mean that in Base you'll have to add an overload of match for every Type you expect to use with Derived . 是的,这确实意味着在Base你必须为你希望与Derived一起使用的每个Type添加一个match重载。

The reason this doesn't work is because the derived class doesn't actually override the virtual function in the base class. 这不起作用的原因是因为派生类实际上并没有覆盖基类中的虚函数。 In order to override a virtual function in a base class a derived class must have exactly the same signature (see note below for an exception). 为了覆盖基类中的虚函数,派生类必须具有完全相同的签名(请参阅下面的注释以了解异常)。 In this case the signatures are different because the virtual function in the base class takes a void * but the function in the derived class takes a type * . 在这种情况下,签名是不同的,因为基类中的虚函数采用void *但派生类中的函数采用type *

Let's make one small change and add the override directive to the function signature in the derived class: 让我们进行一个小的更改,并将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");

}

Here's what happens when we compile now: 这是我们现在编译时会发生的事情:

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; }

Assuming we're using C++11 it's always a good idea to use override to be sure we really are overriding a base class virtual function. 假设我们正在使用C ++ 11,那么使用override来确保我们真的覆盖基类虚函数总是一个好主意。

Note from above: There's something called a covariant return type where an override doesn't have to have the same signature, but that's beyond the scope of the question. 从上面注意:有一种称为协变返回类型的东西,其中覆盖不必具有相同的签名,但这超出了问题的范围。

Thanks to o_weisman 's comment under the original question, I was able to find a working solution using function templates, (see below). 感谢o_weisman在原始问题下的评论,我能够找到一个使用函数模板的工作解决方案,(见下文)。 Admittedly in my solution's current form, I leverage a restriction that each Type-instance of Derived is a singleton. 不可否认,在我的解决方案的当前形式中,我利用了一个限制,即Derived的每个Type-in​​stance都是一个单例。 This works well for my particular design, but the behavior could be expanded as needed. 这适用于我的特定设计,但可以根据需要扩展行为。 One possibility that would allow for multiple instances of Derived might be to check if the 'this' pointer in Base::match is in a set of all instances (tracked in a static set variable updated at construct/destruct time), instead of against a single singleton instance. 允许多个Derived实例的一种可能性是检查Base :: match中的'this'指针是否在所有实例的集合中(在构造/销毁时更新的静态集变量中跟踪),而不是反对单个单例实例。 Anyhow, I hope this helps someone who might be facing a similar design challenge. 无论如何,我希望这可以帮助那些可能面临类似设计挑战的人。

#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");

}

This produces the desired result of: 这产生了以下期望的结果:

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

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

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