我遇到了一个有点太大的问题。搜索它是另一个复杂的问题,因为我不知道该搜索什么。所以我希望有人能给我指明一个正确的方向。
当使用enum实现工厂以拾取作为模板类的变体时,会出现此问题。问题是编译器即使在不可能的情况下也会执行类型检查。最小的例子如下:
template <class T1, class T2>
class Base {
public:
Base() {}
};
template <class T1>
class A : public Base<T1, T1> {
public:
A(): Base<T1, T1>() {}
};
template <class T1, class T2>
class B : public Base<T1, T2> {
public:
B(): Base<T1, T2>() {}
};
enum class Type {
TYPE_A, TYPE_B
};
class Factory {
public:
template<class T1, class T2>
static Base<T1, T2>* Create(Type type) {
switch (type) {
case Type::TYPE_A:
return new A<T1>();
case Type::TYPE_B:
return new B<T1, T2>();
}
return nullptr;
}
};
int main(int argc, char const * argv[]) {
Base<int, double>* ptr = Factory::Create<int, double>(Type::TYPE_B);
delete ptr;
return 0;
}
对于那些你不想运行的人,还有一条错误消息:
test_template_factory.cpp: In instantiation of ‘static Base<T1, T2>* Factory::Create(Type) [with T1 = int; T2 = double]’:
test_template_factory.cpp:38:69: required from here
test_template_factory.cpp:29:26: error: cannot convert ‘A<int>*’ to ‘Base<int, double>*’ in return
return new A<T1>();
^
有办法绕过这个吗?有没有一种方法可以在保持灵活性的同时避免这种情况?提前感谢!
您应该选择在编译时而不是在运行时执行的操作。
像这样的东西可以帮助你。
template <class T1, class T2>
class Base {
public:
Base() {}
};
template <class T1>
class A : public Base<T1, T1> {
public:
A(): Base<T1, T1>() {}
};
template <class T1, class T2>
class B : public Base<T1, T2> {
public:
B(): Base<T1, T2>() {}
};
enum class Type {
TYPE_A, TYPE_B
};
class Factory {
public:
template<Type type, class T1, class T2>
static Base<T1, T2>* Create() {
return create_helper<type, T1, T2>::apply();
}
private:
template<Type type, class T1, class T2>
struct create_helper
{
static Base<T1, T2>* apply() { return nullptr; }
};
};
template<class T1, class T2>
struct Factory::create_helper<Type::TYPE_A, T1, T2>
{
static Base<T1, T2>* apply() { return new A<T1>(); }
};
template<class T1, class T2>
struct Factory::create_helper<Type::TYPE_B, T1, T2>
{
static Base<T1, T2>* apply() { return new B<T1, T2>(); }
};
int main(int argc, char const * argv[]) {
Base<int, double>* ptr = Factory::Create<Type::TYPE_B, int, double>();
delete ptr;
return 0;
}
设计的主要问题是不能为任意T1
和T2
创建A
。想象一下你的代码是
Base<int, double>* ptr = Factory::Create<int, double>(Type::TYPE_A);
这肯定是一个错误,因为可能的A
都不是Base<int, double>
的子类。这是编译器观察到的错误,因为当它实例化Factory::Create<int, double>
时,它还不知道你会把TYPE_B
传递给它
一个可能的解决方案是为Factory::Create<T1,T1>
提供专门化,大致如下:
template<class T1, class T2>
class Factory {
public:
static Base<T1, T2>* Create(Type type);
};
template<class T1>
class Factory<T1,T1> {
public:
static Base<T1, T1>* Create(Type type);
};
template<class T1, class T2>
Base<T1, T2>* Factory<T1,T2>::Create(Type type)
{
switch (type) {
case Type::TYPE_A:
throw "can't create A";
case Type::TYPE_B:
return new B<T1, T2>();
}
return nullptr;
}
template<class T1>
Base<T1, T1>* Factory<T1,T1>::Create(Type type)
{
switch (type) {
case Type::TYPE_A:
return new A<T1>();
case Type::TYPE_B:
return new B<T1, T1>();
}
return nullptr;
}
...
Base<int, int>* ptr1 = Factory<int, int>::Create(Type::TYPE_B);
Base<int, int>* ptr2 = Factory<int, int>::Create(Type::TYPE_A);
Base<int, double>* ptr3 = Factory<int, double>::Create(Type::TYPE_B);
Base<int, double>* ptr4 = Factory<int, double>::Create(Type::TYPE_A); // throws
请注意,我将模板参数移到了类中,以允许部分专用化。