【发布时间】:2017-01-12 19:14:33
【问题描述】:
任何人都可以提出一种技术来避免以下代码中的虚拟模板函数吗?我已经阅读了其他几篇文章,但我不知道如何将这些解决方案应用于这种情况。
我正在构建一个包含模板类层次结构的库。我想创建一个“工厂”函数数组,可用于按名称实例化派生类(例如,基于命令行参数)。
如果可能的话,我希望每个派生类都能够在其自己的 .hpp 或 .cpp 文件中注册自己(而不是必须在某处维护所有可能派生类的单个列表)。
下面的代码几乎可以工作,除了尝试使用虚拟模板函数的致命缺陷。
//
// This code would appear in a library
//
template<class T>
class Base {
public:
Base(const char* /*param*/) {}
};
//
// Description of each derived class.
// We need this non-templated base class so we can store all our
// descriptions in a single vector
//
class DescriptionBase {
private:
const char *description;
const char *name;
public:
DescriptionBase(const char* pDesc, const char* pName) : description(pDesc), name(pName){
// Whenever a Description object is created, it is automatically registered with the
// global descriptionList. This allows us to register derived classes in their own
// .cpp/.hpp file (as opposed to keeping a centralized list of all derived classes).
descriptionList.push_back(this);
}
// FAIL Can't have virtual template functions
virtual template<class T>
Base<T> *make(const char *param) {return new Base<T>(param); }
static vector<DescriptionBase *> descriptionList;
};
//global list of all derived classes
vector<DescriptionBase *> DescriptionBase::descriptionList;
// We use the template to store the type of the derived class
// for use in the make method
template<template<typename> class D>
class Description : public DescriptionBase {
public:
Description(const char* pDesc, const char* pName) : DescriptionBase(pDesc, pName) {}
template<class T>
Base<T> *make(const char *params) {
return new D<T>(params);
}
};
//
// These derived classes may be part of the library, or they may be
// written by users of the library.
//
template<class T>
class DerivedA : public Base<T> {
public:
DerivedA(const char* param) : Base<T>(param) {return;}
};
Description<DerivedA> derivedA("derivedA", "This is the first derived class");
template<class T>
class DerivedB : public Base<T> {
DerivedB(const char* param) : Base<T>(param) {return;}
};
Description<DerivedA> derivedB("derivedA", "This is the second derived class");
//
// Example code written by the user of the library.
//
//
int main(int argc, char *argv[]) {
// Using a descriptionList is just a short-cut here.
// Final code will use a map.
int indexOfDerivedA = 0;
Base<int> *intItem = DescriptionBase::descriptionList[indexOfDerivedA]->make<int>("parameter to derived type's constructor");
Base<char> *charItem = DescriptionBase::descriptionList[indexOfDerivedA]->make<char>("parameter to derived type's constructor");
}
【问题讨论】:
-
如果你不知道返回值的类型,那么你就不能明智地使用构造函数,所以它们要么需要有一个共同的返回类型(这样你就可以将它实际分配给某物) 或者它们应该分别命名为工厂。类型系统无法知道“fred”或“george”是什么类型,但它必须在编译时确定调用是否有效。重要的是要记住 Base
和 Base 从类型系统的角度来看没有任何关系。 -
我没有阅读您的代码,但您可以通过将字符串映射到返回与接口匹配的对象的函子来做到这一点。类通过在该映射中插入一个仿函数来注册自己。
-
@xaxxon 我修改了代码以使其更清晰。 “fred”和“george”是构造函数的参数,而不是类型名称。 Base
和 Base 不打算相关。我在库中构建 descriptionList时遇到了困难(当然,它不知道用户将选择哪些模板参数)。 -
@imreal 使用仿函数的问题是仿函数返回的对象是模板化的。该库可以定义函子,但它不能实例化它们,因为它不知道用户将选择哪些类型作为模板参数。
-
是否有
T的有限有界列表可以传递给Base<T>?您能否至少模糊地描述您正在解决的实际实际问题,看看您是否只是在走死胡同?您想要按名称查找工厂模板吗?需要什么样的链接?我可以请求将多少代码放入常见的 .h 文件中?Base<T>之间究竟有何不同?
标签: c++ templates factory virtual-functions