如何部分重载 C++ 模板子类中的虚函数?
How can I partially overload a virtual function in a C++ template subclass?
我正在尝试创建一个非模板基础 class 来创建一个接口,通过该接口我可以与派生模板 class 交互。我希望使用部分虚函数重载,但有些东西不起作用,我不确定为什么。
谁能解释为什么我使用下面的代码 returns:
B match = False
D match = True
E match = False
而不是 B,D 返回 True,E 返回 False?我原以为派生的 class 中的重载运算符会获取指向 int '&i' 的指针并被调用,但事实并非如此。
需要说明的是,我不是要 -override- match 的 Base 版本,而是要 -overload- 它,特别是希望它有一个不同的,在这种情况下更专业的接口Base 中的一个,在使用其函数签名时接管。
我还试图避免为我可能实例化的 Derived 模板的每种风格扩展 Base class。
甚至是陌生人,我 - 可能 - 在这里会发疯,但我发誓不久前的某个时候这对我有用! Fwiw 我在 OSX 10.5,使用 llvm 7.0.0 和 clang 700.1.76。这可能是编译器的特质吗?
虽然我在这里尝试(未成功)使用部分重载,但我真的对任何解决通过参数类型选择模板实例函数的问题的方法持开放态度,而不会扩散 class es,if-thens/case 或将特定专业化添加到 Base class。如果您有另一种方法我可以加入以实现类似功能,我很想听听。
感谢您提供任何见解!
#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::match
不匹配 Base::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
如果您手动创建 Derived<int>
作为 class,它的成员函数将是:
virtual bool match( const int *data ) const { return true; }
它不会覆盖基础 class。因此,当您使用基 class 指针调用函数时,它会执行基 class 实现,而当您使用派生 class 指针调用它时,它会执行派生 class实施。
如果使用 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的评论
如果你不想覆盖:
- 不要在成员函数声明中使用
virtual
。
使用
将基础class函数引入派生class的范围
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; }
};
因为函数的签名不匹配:
在class Base
你有
virtual bool match( const void *data ) const { return false; }
注意 const void *
参数类型
然后在Derived
你有
virtual bool match( const Type *data ) const { return true; }
这里的 Type
是 int
(来自 main
的 Derived<int> *d
)
只需在 Base 中添加一个带有 const int* data
签名的虚拟,就可以了。
是的,这确实意味着在 Base
中,您必须为希望与 Derived
一起使用的每个 Type
添加 match
的重载。
这不起作用的原因是派生的 class 实际上并未 覆盖 基 class 中的虚函数。为了覆盖基 class 中的虚函数,派生的 class 必须具有 完全 相同的签名(请参阅下面的注释以了解例外情况)。在这种情况下,签名不同,因为基 class 中的虚函数采用 void *
而派生 class 中的函数采用 type *
.
让我们做一个小改动,将 override
指令添加到派生的函数签名中 class:
#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
始终是一个好主意,以确保我们确实覆盖了基 class 虚函数。
上面的注释:有一个叫做 covariant return type 的东西,其中覆盖不必具有相同的签名,但这超出了问题的范围。
感谢 o_weisman 在原始问题下的评论,我能够使用函数找到一个可行的解决方案模板,(见下文)。不可否认,在我的解决方案的当前形式中,我利用了 Derived 的每个类型实例都是单例的限制。这适用于我的特定设计,但可以根据需要扩展行为。允许 Derived 的多个实例的一种可能性是检查 Base::match 中的 'this' 指针是否在所有实例的集合中(在 construct/destruct 时间更新的静态集合变量中跟踪),而不是针对单个单例实例。无论如何,我希望这对可能面临类似设计挑战的人有所帮助。
#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
我正在尝试创建一个非模板基础 class 来创建一个接口,通过该接口我可以与派生模板 class 交互。我希望使用部分虚函数重载,但有些东西不起作用,我不确定为什么。 谁能解释为什么我使用下面的代码 returns:
B match = False
D match = True
E match = False
而不是 B,D 返回 True,E 返回 False?我原以为派生的 class 中的重载运算符会获取指向 int '&i' 的指针并被调用,但事实并非如此。
需要说明的是,我不是要 -override- match 的 Base 版本,而是要 -overload- 它,特别是希望它有一个不同的,在这种情况下更专业的接口Base 中的一个,在使用其函数签名时接管。
我还试图避免为我可能实例化的 Derived 模板的每种风格扩展 Base class。
甚至是陌生人,我 - 可能 - 在这里会发疯,但我发誓不久前的某个时候这对我有用! Fwiw 我在 OSX 10.5,使用 llvm 7.0.0 和 clang 700.1.76。这可能是编译器的特质吗?
虽然我在这里尝试(未成功)使用部分重载,但我真的对任何解决通过参数类型选择模板实例函数的问题的方法持开放态度,而不会扩散 class es,if-thens/case 或将特定专业化添加到 Base class。如果您有另一种方法我可以加入以实现类似功能,我很想听听。
感谢您提供任何见解!
#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::match
不匹配 Base::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
如果您手动创建 Derived<int>
作为 class,它的成员函数将是:
virtual bool match( const int *data ) const { return true; }
它不会覆盖基础 class。因此,当您使用基 class 指针调用函数时,它会执行基 class 实现,而当您使用派生 class 指针调用它时,它会执行派生 class实施。
如果使用 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的评论
如果你不想覆盖:
- 不要在成员函数声明中使用
virtual
。 使用
将基础class函数引入派生class的范围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; } };
因为函数的签名不匹配:
在class Base
你有
virtual bool match( const void *data ) const { return false; }
注意 const void *
参数类型
然后在Derived
你有
virtual bool match( const Type *data ) const { return true; }
这里的 Type
是 int
(来自 main
的 Derived<int> *d
)
只需在 Base 中添加一个带有 const int* data
签名的虚拟,就可以了。
是的,这确实意味着在 Base
中,您必须为希望与 Derived
一起使用的每个 Type
添加 match
的重载。
这不起作用的原因是派生的 class 实际上并未 覆盖 基 class 中的虚函数。为了覆盖基 class 中的虚函数,派生的 class 必须具有 完全 相同的签名(请参阅下面的注释以了解例外情况)。在这种情况下,签名不同,因为基 class 中的虚函数采用 void *
而派生 class 中的函数采用 type *
.
让我们做一个小改动,将 override
指令添加到派生的函数签名中 class:
#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
始终是一个好主意,以确保我们确实覆盖了基 class 虚函数。
上面的注释:有一个叫做 covariant return type 的东西,其中覆盖不必具有相同的签名,但这超出了问题的范围。
感谢 o_weisman 在原始问题下的评论,我能够使用函数找到一个可行的解决方案模板,(见下文)。不可否认,在我的解决方案的当前形式中,我利用了 Derived 的每个类型实例都是单例的限制。这适用于我的特定设计,但可以根据需要扩展行为。允许 Derived 的多个实例的一种可能性是检查 Base::match 中的 'this' 指针是否在所有实例的集合中(在 construct/destruct 时间更新的静态集合变量中跟踪),而不是针对单个单例实例。无论如何,我希望这对可能面临类似设计挑战的人有所帮助。
#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