带有模板的基于枚举的工厂无法转换类型



我遇到了一个有点太大的问题。搜索它是另一个复杂的问题,因为我不知道该搜索什么。所以我希望有人能给我指明一个正确的方向。

当使用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;
}

设计的主要问题是不能为任意T1T2创建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

请注意,我将模板参数移到了类中,以允许部分专用化。

最新更新