如何正确实现模板化产品的工厂
How to properly implement a factory for templated products
我认为这个任务是很常见的,但我找不到一个合适的解决方案。
我有一个层次结构的"产品",其中有一些"特征",所以我决定使用模板接口的产品,其中模板参数是"特质":
这些是特征:
struct Foo {
static std::string get_name() { return "Foo"; }
Foo(int a) : a_(a) {}
int operator()() const { return a_; }
private:
int a_;
};
struct Bar {
static std::string get_name() { return "Bar"; }
Bar(int a, int b) : a_(a), b_(b) {}
int operator()() const { return a_ + b_; }
private:
int a_;
int b_;
};
struct Spam {
Spam(int a, int b) : a_(a), b_(b), c_(0) {}
void operator()() { c_++; }
private:
int a_;
int b_;
int c_;
};
这些是产品层次结构:
template <class T>
class Product {
public:
typedef T T_type;
virtual T get() = 0;
virtual ~Product() {}
};
template <class T>
class ProductA : public Product<T> {
typedef Product<T> base_type;
public:
ProductA(int a) : a_(a) {}
virtual ~ProductA() {}
virtual T get() {
return T(a_);
}
private:
int a_;
};
template <class T, class U>
class ProductB : public Product<T> {
typedef Product<T> base_type;
public:
typedef U U_type;
ProductB(int a, int b) : a_(a), b_(b) {}
virtual ~ProductB();
virtual T get() {
init(); // U is being used here
return T(a_, b_);
}
protected:
void init() {}
private:
int a_;
int b_;
};
我需要使用额外的继承级别,因为ProductA
和ProductB
的不同接口——它们有不同的c-tor。
以下是具体产品:
class ProductA1 : public ProductA<Foo> {
typedef ProductA<Foo> base_type;
public:
ProductA1(int a) : base_type(a) { std::cout << "A1 created" << std::endl; }
virtual ~ProductA1() { std::cout << "A1 deleted" << std::endl; }
};
class ProductB1 : public ProductB<Bar, Spam> {
typedef ProductB<Bar, Spam> base_type;
public:
ProductB1(int a, int b) : base_type(a, b) { std::cout << "B1 created" << std::endl; }
virtual ~ProductB1() { std::cout << "B1 deleted" << std::endl; }
};
现在我想有一个"统一"创建产品的机制(在这个例子中有两个类型ProductA1
和ProductB1
),以某种方式将字符串传递到某个方法中。显然我需要工厂…
因此,我为层次结构的不同分支实现了工厂(以创建ProductA
和ProductB
类型的对象),以便我可以创建通过模板参数传递其类型的对象:
template <class P>
struct ProductAFactory {
typedef typename P::T_type T_type;
typedef ProductA<T_type> product_type;
static
product_type* create(int a) {
return new P(a);
}
};
template <class P>
struct ProductBFactory {
typedef typename P::T_type T_type;
typedef typename P::U_type U_type;
typedef ProductB<T_type,
U_type> product_type;
static
product_type* create(int a, int b) {
return new P(a, b);
}
};
有了这些工厂,我必须有一个工厂应该构造必要类型的产品,并返回一个指向Product<T>
接口的产品的指针:
template <class T>
class ProductFactory {
public:
static
Product<T>*
create(const std::string& product_name,
const int a,
const int b) {
const std::string product_a1 = "A1";
const std::string product_b1 = "B1";
if (product_name == product_a1)
return ProductAFactory<ProductA1>::create(a);
else if (product_name == product_b1)
return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error
else
throw std::runtime_error("Unsupported product: " + product_name);
}
};
所有这些代码的目的是这样使用:
void main() {
typedef Foo T;
std::shared_ptr<Product<T>> p(ProductFactory<T>::create("A1", 1, 1));
T t = p->get();
std::cout << t.get_name() << ": " << t() << std::endl;
}
在这里我遇到了一个编译这段代码的问题——错误是return value type does not match the function type
在(*)
。似乎ProductB<Foo, Spam>
不能自动转换为它的基类型Product<Foo>
…
我不是一个好的工厂开发人员,也许我不理解基本的原则和概念。有人能帮助纠正这个代码或这种方法吗?谢谢!
当重新创建这个时,我得到的错误是(与您发布的略有不同):
error: cannot convert 'ProductBFactory<ProductB1>::product_type* {aka ProductB<Bar, Spam>*}' to 'Product<Foo>*' in return
return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error
问题的关键在于,您试图从声明返回Product<Foo>*
的函数返回ProductB<Bar, Spam>*
。你似乎认为这些类型在某种程度上与继承有关,但它们根本不是。ProductB<Bar, Spam>
实际上是Product<Bar>
的一个子类——最后一个与Product<Foo>
没有任何关系或可转换,就像vector<int>
与vector<float>
没有任何关系一样——也就是说,根本没有。具有不同模板参数的模板类是完全不同的。
所以要么你在你的代码中犯了一个错误,并试图返回一个ProductB<Bar,Spam>*
,而实际上你应该试图返回一个ProductB<Foo,Spam>*
(说),或者你误解了模板继承层次结构之间的多态关系(或缺乏)与不同的模板参数。
UPDATE:回复你的评论:可以创建一个工厂来创建不同模板类型的对象,只要它们在类型层次结构中有一个共同的祖先,可以用作返回类型。在您的情况下,这需要至少在某种程度上重新设计。一种方法是为Product<T>
创建一个基类,比如ProductBase
,它不依赖于模板:
template <class T>
class Product : public ProductBase
{
...
};
那么你的create
函数的返回类型可以是ProductBase*
。同样,您可以从Product
本身的定义中取出模板。无论哪种方式,这都需要对接口进行一些进一步的更改以使其有用。
例如,请参阅这个现场演示,我在其中实现了ProductBase
方法。因此,您的示例可以编译,但这只是由于在主函数中引入了一个讨厌的static_cast
。这类问题必须通过适当地更改基本接口来解决。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- CMake-按正确顺序将项目与C运行时对象文件链接
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 用于访问容器<T>数据成员的正确 API
- 如何使用Luacneneneba API正确读取字符串和表参数
- 如何使用用户输入在C++中正确填充2D数组
- node-gyp 在 macOS 上未正确链接库
- C++从另一个类访问公共静态向量的正确方法是什么
- 如何在 C 中正确使用 libiconv 使其不会报告"Arg list too long"?
- 为什么我的for循环不能正确获取argv
- 如何取消对nullptr的屏蔽,返回正确的对象
- AES加密到解密未正确输出
- 使用C++程序合并排序没有得到正确的输出
- 在 c++ 中拥有一组结构的正确方法是什么?
- 下面抽象工厂设计模式的实现是正确的吗
- 从工厂返回时,可以正确的方法来upcast superty_ptr
- unique_ptr和前向声明:编写工厂函数的正确方法
- 工厂类正确使用std-move
- 工厂模式的正确方法是什么?
- 如何正确实现模板化产品的工厂