带有抽象类的覆盖模板功能
Override template function with abstract class
我创建了一个模板函数,以下定义。
template<class T>
void func(T t){ /* do stuff */ }
如果我从我制作的抽象类继承。
,我想超载此模板。class A {
public:
virtual void doStuff() = 0;
};
class B : public A {
virtual void doStuff(){ /* do stuff */ }
}
我尝试使用模板专业化(下图(,但仍在使用原始定义。
template<>
void func(A& a){ /* do different stuff */ } // Not called by func(B())
我也尝试过超载它,尽管与INT一起使用,但它与我的基类无法使用。
func(int i){ /* do stuff with i */ } // Called by func(3)
func(A& a){ /* do different stuff */ } // Not called by func(B())
我猜想这与C 不希望将我的B实例隐式施加给A并引用它有关,但是我找不到任何解释如何解决此行为的东西。由于A具有纯虚拟函数,因此我不能仅定义func(A a)
。任何帮助将不胜感激。
这是一个可以复制我所经历的行为的示例。
#include <iostream>
template<class T>
void func(T t){
std::cout << "Template function called!" << std::endl;
}
class A {
public:
virtual void doStuff() = 0;
};
class B : public A{
public:
virtual void doStuff(){};
};
template<>
void func(const A& a){
std::cout << "Specialized template called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
B b{};
func(b);
return 0;
}
如果您可以访问C 17,则可以创建一个将转发到正确函数的公共过载:
namespace detail {
template<class T>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a){
std::cout << "Overload called!" << std::endl;
}
}
template<class T>
void func(T& t) {
if constexpr (std::is_base_of_v<A, T>) {
detail::func(static_cast<A&>(t));
} else {
detail::func(t);
}
}
int main() {
B b;
func(b);
}
否则,您可以使用标签调度或Sfinae:
标签调度:
template<class T>
void func(T t, std::false_type) {
std::cout << "Template function called!" << std::endl;
}
void func(A& a, std::true_type) {
std::cout << "Other function called!" << std::endl;
}
template<class T>
void func(T& t) {
func(t, std::is_base_of<A, T>{});
}
sfinae:
template<class T,
std::enable_if_t<std::is_base_of_v<A, T>>* = nullptr>
void func(T t) {
std::cout << "Template function called!" << std::endl;
}
template<class T,
std::enable_if_t<!std::is_base_of_v<A, T>>* = nullptr>
void func(T& t) {
std::cout << "Other function called!" << std::endl;
}
c 20解决方案
您可以在此处运行代码。
使用C 20的概念,我们可以编写inherits_from
概念,并使用它。概念使我们能够约束模板,以便它仅适用于表达式为真的情况。
这个概念看起来像这样:
#include <type_traits>
template<class Derived, class Base>
concept derived_from = std::is_base_of_v<Base, Derived>;
然后,我们可以编写通用模板和约束模板:
struct MyBase{};
struct MyDerived : MyBase{};
// This is the generic template; using auto here is valid in C++20
void do_thing(auto const& thing) {
std::cout << "Doing thing on regular typen";
}
//This is the template that acts on classes derived from MyBase
void do_thing(derived_from<MyBase> const& x) {
std::cout << "Doing thing on MyBasen";
}
由于第二个功能将T
声明为以下概念inherits_from
,因此更专业,因此对于实际从MyBase
继承的类型,它将通过通用模板选择:
int main() {
do_thing(10); // Prints "Doing thing on regular type"
do_thing(MyBase()); // Prints "Doing thing on MyBase"
do_thing(MyDerived()); // Prints "Doing thing on MyBase"
}
C 17解决方案
您可以在此处运行代码。
我们可以使用SFINAE模拟概念的行为,尽管这需要修改通用模板,以便如果T
扩展了MyBase
,则可以忽略它。使用SFINAE的关键是如果条件是错误的,则触发替换故障,从而导致该超载被忽略。
为了触发替换故障,请在模板参数列表末尾添加默认的模板参数。在我们的情况下,看起来像这样:
template<
class T,
// This defaulted argument triggers the substitution failure
class = std::enable_if_v</* condition */>>
在我们的代码中, - 如果T
扩展了MyBase
,则通用超载将被禁用 - 如果T
不扩展MyBase
看起来像这样:
struct MyBase {};
struct MyDerived : MyBase {};
template<
class T,
class = std::enable_if_t<!std::is_base_of_v<MyBase, T>>>
void do_thing(T const&) {
std::cout << "Doing thing on regular typen";
}
// This overload is *disabled* if T doesn't inherit from MyBase
template<
class T,
// We have to have an additional defaulted template argument to distinguish between the overloads
class = void,
class = std::enable_if_t<std::is_base_of_v<MyBase, T>>>
void do_thing(T const& x) {
std::cout << "Doing thing on MyBasen";
}
尽管声明很奇怪,但我们仍然可以使用do_thing
,就好像它是常规功能:
int main() {
do_thing(10);
do_thing(MyBase());
do_thing(MyDerived());
}
将事物送给C 11
您可以在此处运行代码。
我们只需要对C 11的Backport事物进行一些较小的更改。基本上,
-
is_base_of_v<MyBase, T>
必须由is_base_of<MyBase, T>::value
代替, -
enable_if_t</* condition */>
必须由typename enable_if</* condition */>::type
代替
B b;
func((A&)b);
我认为您不能在这里通过临时性。您不能将b((铸造为" a&amp;"因为它是一个rvalue,并将其投入到consta&amp;使模板版本成为更好的匹配。
其他选项(在其他答案之上(使用C 11-如果T是B:
的子类型,则明确删除模板版本template<class T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type
func(T& t){
std::cout << "Template function called!" << std::endl;
}
void func(const A& a){
std::cout << "Overload called!" << std::endl;
}
int main(){
func(B());
}
- C++中"覆盖功能的异常规范比基本版本更宽松"的奇怪错误
- 矢量被push_back功能覆盖
- 为什么我无法覆盖虚拟功能?
- 是否可以在 C++03 中实现类似"覆盖"的功能
- 覆盖私有功能,它与受保护功能有何不同?
- 带有抽象类的覆盖模板功能
- 覆盖私人功能
- C++继承 - 覆盖功能,包括使用 "::" s、.h 文件和.cpp文件
- C++向下转换以撤消功能覆盖
- 每个实例C 的覆盖功能
- SVML的覆盖功能调用
- 如何在标题文件中获取成员功能的正确代码覆盖范围
- C 从同一基本模板类覆盖功能,具有多个继承模棱两可的函数调用
- 是否有可能覆盖全局实现的功能
- variadic模板和功能覆盖
- 需要有关多个继承和可选功能覆盖C 的建议
- 我想我将名称隐藏与功能覆盖混淆了
- 虚拟功能覆盖和隐藏
- 纯虚拟功能覆盖
- 将私有功能覆盖给公共功能的潜在危险是什么?