你问的是动态函数,但你的主要问题不在那里,而是在这里:
通过该接口公开的部分函数的返回值由输入参数sKeyName决定。
C++ 是一种静态类型语言,这意味着您不能为依赖于参数值的函数提供返回类型。暂时忽略继承,您提供的代码要求用户确定返回的数组的类型独立传递的参数:
struct SimpleDataAccess {
template <typename T>
array2d<T>* get_data( std::string const & which ) {
return new array2d<T>();
}
};
int main() {
SimpleDataAccess accessor;
array2d<int> = accessor.get<int>( "int" ); // <int> at the place of call fixes
// the return type, not "int" !
}
现在,如果您愿意接受这种情况(即调用者将知道并设置返回类型),则有不同的方法可以为您的语言不允许模板化虚拟函数的特定问题提供解决方法。想到的第一件事很好,因为它也遵循 NVI 习惯用法(并显示了它的重要性):为数据提供非虚拟公共模板化访问器,并根据固定返回类型的虚函数实现它。
class DataAccessor {
virtual Type get_data_impl( std::string const & ) = 0;
public:
template <typename T>
array2d<T>* get_data( std::string const & which ) {
Type tmp = get_data_impl( which );
return convert( tmp );
}
};
假设我们可以解决Type 和convert 是什么,我们就有了解决方案。这是 NVI 惯用语的一个很好的例子:用户提供的接口(公共的、非虚拟的)与扩展所需的接口(私有的、虚拟的)不同。这两个合约不同,您的用户要求您提供指向特定具体 array2d 实例化的指针,但该语言不允许您从扩展中要求相同的合约,但这不是问题,因为它们是不同的 接口。
现在回到Type 和convert。这两者是相关的,您可以采用不同的方法。最简单的实现是拥有一个array2d_base 类,所有array2d<T> 都从该类派生(通过提供一个启用RTTI 的虚拟析构函数):
struct array2d_base {
virtual ~array2d_base() {}
};
template <typename T>
class array2d : public array2d_base {
// implementation
};
// Type == array2d_base*
// convert == dynamic_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
return dynamic_cast< array2d<T>* >( get_data_impl( s ) );
}
如果您不能扩展或修改array2d 类,那么您可以通过类型擦除获得类似的结果。这将具有在array2d 中不需要RTTI 的优点,而仅在类型擦除支持中。最简单的此类实现是在内部接口中使用boost::any:
// Type == boost::any
// convert == boost::any_cast< array2d<T>* >
template <typename T>
array2d<T>* DataAccessor::get_data( std::string const & s ) {
boost::any tmp = get_data_impl(s);
return boost::any_cast< array2d<T>* >( tmp );
}