C ++不支持模板化的虚拟成员函数,但我有一个理想的场景。我想知道是否有人想办法实现这一目标。
#include <iostream>
class Foo {
public:
virtual void bar(int ){}
// make a clone of my existing data, but with a different policy
virtual Foo* cloneforDB() = 0;
};
struct DiskStorage {
static void store(int x) { std::cout << "DiskStorage:" << x << "\n"; }
};
struct DBStorage {
static void store(int x) { std::cout << "DBStorage:" << x << "\n"; }
};
template<typename Storage>
class FooImpl : public Foo {
public:
FooImpl():m_value(0) {}
template<typename DiffStorage>
FooImpl(const FooImpl<DiffStorage>& copyfrom) {
m_value = copyfrom.m_value;
}
virtual void bar(int x) {
Storage::store(m_value);
std::cout << "FooImpl::bar new value:" << x << "\n";
m_value = x;
}
virtual Foo* cloneforDB() {
FooImpl<DBStorage> * newfoo = new FooImpl<DBStorage>(*this);
return newfoo;
}
int m_value;
};
int main()
{
Foo* foo1 = new FooImpl<DiskStorage>();
foo1->bar(5);
Foo* foo2 = foo1->cloneforDB();
foo2->bar(21);
}
现在,如果我想克隆Foo implmemeation,但使用不同的Storagepolicy,我必须明确说明每个这样的实现:
cloneforDB()
cloneforDisk()
模板参数会简化它。 谁能想到更清洁的方法呢? 请关注这个想法而不是示例,因为它显然是一个人为的例子。
答案 0 :(得分:8)
通常,如果要使用虚拟模板方法,则意味着类层次结构的设计出现问题。高级原因如下。
模板参数必须在编译时知道,这就是它们的语义。它们用于保证代码的健全性。
虚函数用于多态,即。在运行时动态调度。
所以你不能将静态属性与运行时调度混合在一起,如果从整体上看它就没有意义。
在这里,您在某处存储某些内容的事实不应该是您的方法的类型的一部分,因为它只是一个行为特征,它可能在运行时更改。因此,错误将该信息包含在方法类型中。
这就是为什么C ++不允许这样做:你必须依靠多态来实现这样的行为。
一个简单的方法是将指针作为参数传递给Storage
对象(如果你只想为每个类提供一个对象,则为单例),并在虚函数中使用该指针。 / p>
这样,您的类型签名不依赖于方法的特定行为。您可以在运行时更改存储(在此示例中)策略,这是您应该要求的良好实践。
有时,行为可以由模板参数(例如Alexandrescu的策略模板参数)决定,但它是在类型级别,而不是方法级别。
答案 1 :(得分:2)
只需使用模板:
class Foo {
public:
virtual void bar(int ){}
template <class TargetType>
Foo* clonefor() const;
};
class FooImpl { ... };
template
inline <class TargetType>
Foo* Foo::clonefor() const
{
return new FooImpl<TargetType>(*this);
}
现在打电话给:
int main()
{
Foo* foo1 = new FooImpl<DiskStorage>();
foo1->bar(5);
Foo* foo2 = foo1->clonefor<DBStorage>();
foo2->bar(21);
}
答案 2 :(得分:1)
我有时习惯解决这个问题的一个技巧是:
template<typename T>
using retval = std::vector<T const*>;
struct Bob {};
// template type interface in Base:
struct Base {
template<typename T>
retval<T> DoStuff();
virtual ~Base() {};
// Virtual dispatch so children can implement it:
protected:
virtual retval<int> DoIntStuff() = 0;
virtual retval<double> DoDoubleStuff() = 0;
virtual retval<char> DoCharStuff() = 0;
virtual retval<Bob> DoBobStuff() = 0;
};
// forward template interface through the virtual dispatch functions:
template<> retval<int> Base::DoStuff<int>() { return DoIntStuff(); }
template<> retval<double> Base::DoStuff<double>() { return DoDoubleStuff(); }
template<> retval<char> Base::DoStuff<char>() { return DoCharStuff(); }
template<> retval<Bob> Base::DoStuff<Bob>() { return DoBobStuff(); }
// CRTP helper so the virtual functions are implemented in a template:
template<typename Child>
struct BaseHelper: public Base {
private:
// In a real project, ensuring that Child is a child type of Base should be done
// at compile time:
Child* self() { return static_cast<Child*>(this); }
Child const* self() const { return static_cast<Child const*>(this); }
public:
virtual retval<int> DoIntStuff() override final { self()->DoStuff<int>(); }
virtual retval<double> DoDoubleStuff() override final { self()->DoStuff<double>(); }
virtual retval<char> DoCharStuff() override final { self()->DoStuff<char>(); }
virtual retval<Bob> DoBobStuff() override final { self()->DoStuff<Bob>(); }
};
// Warning: if the T in BaseHelper<T> doesn't have a DoStuff, infinite
// recursion results. Code and be written to catch this at compile time,
// and I would if this where a real project.
struct FinalBase: BaseHelper<FinalBase> {
template<typename T>
retval<T> DoStuff() {
retval<T> ret;
return ret;
}
};
我从基于模板的调度,到虚拟函数调度,再到基于模板的调度。
接口是我要发送的类型的模板。一组有限的此类类型通过虚拟调度系统转发,然后在编译时重新分配到实现中的单个方法。
我承认这很烦人,并且能够说“我希望这个模板是虚拟的,但只有以下类型”才会很好。
这有用的原因是它允许你编写与这些方法一致操作的类型无关的模板粘合代码,而不必像传递方法之类的指针那样做,或者编写类型特征包提取要调用的方法。