覆盖接口中的模板成员
Posted
技术标签:
【中文标题】覆盖接口中的模板成员【英文标题】:Override template member in Interface 【发布时间】:2014-03-15 10:56:00 【问题描述】:是否可以使用可以在派生类中覆盖的模板方法声明某种类型的基类?下面的例子:
#include <iostream>
#include <stdexcept>
#include <string>
class Base
public:
template<typename T>
std::string method() return "Base";
;
class Derived : public Base
public:
template<typename T>
std::string method() override return "Derived";
;
int main()
Base *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
我希望 Derived
作为输出,但它是 Base
。我认为有必要制作一个模板化的包装类,它接收实现类作为模板参数。但我想确定一下。
【问题讨论】:
我第一眼看到的一个基本问题:你的代码缺少virtual
不能定义虚拟模板方法。
override
仅适用于虚拟方法,并且您只能覆盖具有相同签名的方法。 method<bool>()
与 method<int>()
不同。
【参考方案1】:
1) 你的函数,为了是多态的,应该用 virtual
标记2) 模板函数在 POI 处实例化并且不能是虚拟的(签名是什么?您保留多少 vtable 条目?)。 模板函数是一种编译时机制,虚函数是运行时机制。
一些可能的解决方案包括:
更改设计(推荐) 采用另一种方法,例如Andrei Alexandrescu (http://www.icodeguru.com/CPP/ModernCppDesign/0201704315_ch11.html) 的多方法【讨论】:
所以某种包装是唯一的解决方案? 更改设计是什么意思?有一些常见的模式吗? 我的意思是模板是一个编译时构造,如果你需要运行时类型识别然后编译时类型识别你很可能因为混合了这两个概念而做错了什么 【参考方案2】:模板方法不能是虚拟的。一种解决方案是使用静态多态性来模拟“模板虚拟”方法的行为:
#include <iostream>
#include <stdexcept>
#include <string>
template<typename D>
class Base
template<typename T>
std::string _method() return "Base";
public:
template<typename T>
std::string method()
return static_cast<D&>(*this).template _method<T>();
;
class Derived : public Base<Derived>
friend class Base<Derived>;
template<typename T>
std::string _method() return "Derived";
public:
//...
;
int main()
Base<Derived> *b = new Derived();
std::cout << b->method<bool>() << std::endl;
return 0;
其中method
是接口,_method
是实现。为了模拟纯虚方法,_method
将不在 Base
中。
不幸的是,这种方式 Base
更改为 Base<Derived>
所以你不能再例如有一个Base*
的容器。
另请注意,对于 const
方法,static_cast<D&>
更改为 static_cast<const D&>
。同样,对于右值引用 (&&
) 方法,它会更改为 static_cast<D&&>
。
【讨论】:
【参考方案3】:使您的示例按预期工作的另一种可能方法是使用std::function
:
class Base
public:
Base()
virtualFunction = [] () -> string return "Base"; ;
template <class T> string do_smth() return virtualFunction();
function<string()> virtualFunction;
;
class Derived : public Base
public:
Derived()
virtualFunction = [] () -> string return "Derived"; ;
;
int main()
auto ptr = unique_ptr<Base>(new Derived);
cout << ptr->do_smth<bool>() << endl;
这会输出“派生”。我不确定这是否是你真正想要的,但我希望它会对你有所帮助..
【讨论】:
有趣的想法。如果有其他方法(在这种情况下是),则不会使用,但请记住。谢谢。 这可能有潜力,但该示例对类型 T 没有任何作用,这是函数模板的全部要点。最好演示一下这种技术如何使派生类能够使用具有不同类型 T 的参数覆盖 do_smth。 我想我是通过向 do_smth 添加一个类型为 T 的参数然后在调用 virtualFunction 之前将参数值保存到一个名为 m_t 的类成员变量来完成这项工作的,它的 lambda 闭包 [&] 然后捕获该值吨。 m_t 的类型是基类,每个 lambda 表达式都可以使用 static_cast 将其强制转换为特定的派生类类型。【参考方案4】:我遇到了同样的问题,但实际上我想出了一个可行的解决方案。展示解决方案的最佳方式是通过示例:
我们想要的(不起作用,因为您不能拥有虚拟模板):
class Base
template <class T>
virtual T func(T a, T b) ;
class Derived
template <class T>
T func(T a, T b) return a + b; ;
int main()
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
解决方案(打印3HelloWorld0.3
):
class BaseType
public:
virtual BaseType* add(BaseType* b) return ; ;
;
template <class T>
class Type : public BaseType
public:
Type(T t) : value(t) ;
BaseType* add(BaseType* b)
Type<T>* a = new Type<T>(value + ((Type<T>*)b)->value);
return a;
;
T getValue() return value; ;
private:
T value;
;
class Base
public:
virtual BaseType* function(BaseType* a, BaseType* b) return ; ;
template <class T>
T func(T a, T b)
BaseType* argA = new Type<T>(a);
BaseType* argB = new Type<T>(b);
BaseType* value = this->function(argA, argB);
T result = ((Type<T>*)value)->getValue();
delete argA;
delete argB;
delete value;
return result;
;
;
class Derived : public Base
public:
BaseType* function(BaseType* a, BaseType* b)
return a->add(b);
;
;
int main()
Base* obj = new Derived();
std::cout << obj->func(1, 2) << obj->func(std::string("Hello"), std::string("World")) << obj->func(0.2, 0.1);
return 0;
我们使用BaseType
类来表示您通常在模板中使用的任何数据类型或类。您将在模板中使用的成员(可能还有运算符)在此处使用虚拟标签进行描述。请注意,为了使多态起作用,指针是必需的。
Type
是扩展Derived
的模板类。这实际上代表了一个特定的类型,例如Type<int>
。这个类非常重要,因为它允许我们将任何类型转换为BaseType
。我们在BaseType
中描述的成员的定义在这里实现。
function
是我们要重写的函数。我们不使用真正的模板,而是使用指向BaseType
的指针来表示类型名。实际的模板函数在定义为func
的Base
类中。它基本上只是调用function
并将T
转换为Type<T>
。如果我们现在从 Base
扩展并覆盖 function
,则会为派生类调用新的覆盖函数。
【讨论】:
以上是关于覆盖接口中的模板成员的主要内容,如果未能解决你的问题,请参考以下文章