我有一个带有模板功能的课程:
foo.h中:
class Foo {
public:
int some_function();
bool some_other_function(int a, const Bar& b) const;
template<typename T>
int some_template_function(const T& arg);
};
template<typename T>
int Foo::some_template_function(const T& arg){
/*...generic implementation...*/
}
现在我已经到了一个我希望能够通过代理类访问Foo的地方,就像在Proxy design pattern中一样。
直观地说,我想重构如下(以下代码不正确,但它表达了我的“理想化”API):
FooInterface.h:
class FooInterface {
public:
virtual int some_function()=0;
virtual bool some_other_function(int a, const Bar& b) const=0;
template<typename T>
virtual int some_template_function(const T& arg)=0;
};
FooImpl.h:
#include "FooInterface.h"
/** Implementation of the original Foo class **/
class FooImpl : public FooInterface {
public:
int some_function();
bool some_other_function(int a, const Bar& b) const;
template<typename T>
int some_template_function(const T& arg);
};
template<typename T>
int FooImpl::some_template_function(const T& arg){
/*...generic implementation...*/
}
FooProxy.h:
#include "FooInterface.h"
class FooProxy : public FooInterface{
protected:
FooInterface* m_ptrImpl; // initialized somewhere with a FooImpl*; unimportant in the context of this question
public:
int some_function()
{ return m_ptrImpl->some_function(); }
bool some_other_function(int a, const Bar& b) const
{ return m_ptrImpl->some_other_function(a,b); }
template<typename T>
int some_template_function(const T& arg)
{ return m_ptrImpl->some_template_function(arg); }
};
但是这段代码惨遭失败。
首先,FooImpl
无法编译,因为类模板函数不能是虚拟的。
更重要的是,即使我使用some_template_function
的定义,即使我将其重新安排到具体的班级或其他陪审团操纵,它仍然会对整体造成严重破坏首先要有一个代理类,因为模板代码需要在头文件中定义并包含在内。这会强制FooProxy.h
包含FooImpl.h
,而FooImpl.h
需要实施some_template_function
所需的所有实施细节和文件包含。因此,如果我使用代理模式来模糊实现细节,使自己与具体实现保持距离,并避免不必要的文件包含,那么我运气不好。
有没有办法将代理模式或其某些变体应用于具有模板函数的类?或者在C ++中这是不可能的吗?
上下文:目前,我正在尝试为一组具有预先存在的内置日志记录机制的类提供代理访问。我对此日志唯一的API使用可变参数模板,因此无法预测它将与之一起使用的参数组合。我希望使用代理实现和客户端之间的分离尽可能干净,我需要最小化从客户端到实现的依赖关系,但我确实需要它们写入相同的日志。
然而,除了我眼前的问题,我对此问题感兴趣。让我感到困惑的是,模板将这样的漏洞推入了一个主要的设计模式,并且我没有在任何地方找到这个问题。
答案 0 :(得分:2)
具有模板化接口的类的包装器/代理将始终要求模板类的定义在调用包装器的代码的头文件中可见。这是因为为模板化接口生成的代码取决于调用它的参数类型。
如果您仍然坚持使用现有的模板化实现FooImpl
,那么正如@mars在评论中所写,您唯一的选择是:
template <class Implementation>
class FooProxy
{
Implementation * m_ptrImpl;
//...
};
如果你可以改变现有的实现,理想的解决方案是重构模板化方法并将它们分成两层;一个层依赖于参数类型,第二个层不依赖于参数类型。在所有实现中,依赖于参数类型的现有方法中的代码应该是相同的,因此可以将此层移动到抽象接口类的方法中。不依赖于参数类型的其余代码可以保留在实现类的非模板化方法中,这意味着实现细节可以隐藏在.cpp文件中。
以下是一个示例,基于支持编写任意类型的日志的场景:
LogInterface.h
class LogInterface {
public:
template<typename T>
void write(const T& arg)
{
// converts from 'T' to array of characters.
// calls non-template 'write' as many times as necessary.
}
virtual void write(const char* p, std::size_t n)=0;
};
LogImpl.h
#include "LogInterface.h"
/** Implementation of the original Log class **/
class LogImpl : public LogInterface {
public:
void write(const char* p, std::size_t n);
};
LogProxy.h
#include "LogInterface.h"
class LogProxy : public LogInterface{
protected:
LogInterface* m_ptrImpl; // initialized somewhere with a LogImpl*
public:
void write(const char* p, std::size_t n)
{ m_ptrImpl->write(p, n); }
};