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

时间:2015-12-15 13:39:37

标签: c++ templates c++11 enums factory

我遇到的问题对我来说有点太大了。搜索它是另一个复杂的问题,因为我无法想象究竟要搜索什么。所以我希望有人能指出我正确的方向。

当使用枚举实现工厂来挑选作为模板类的变体时,会出现问题。问题是即使对于不可能的情况,编译器也会执行类型检查。最小的例子如下:

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>();
                          ^

有解决方法吗?有没有一种方法可以避免它同时保持灵活性?提前谢谢!

2 个答案:

答案 0 :(得分:2)

您应该选择在编译时执行的操作,而不是在运行时。

这样的事情可以帮助你。

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;
}

答案 1 :(得分:1)

您的设计的主要问题是您无法为任意 AT1创建T2。想象一下你的代码是

Base<int, double>* ptr = Factory::Create<int, double>(Type::TYPE_A);

这绝对是一个错误,因为A&#39}都不是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

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