5

假设我有一个类模板,它有一个成员pData,它是一个AxB任意类型的数组T

template <class T> class X{ 
public:
    int A;
    int B;
    T** pData;
    X(int a,int b);
    ~X();        
    void print(); //function which prints pData to screen

};  
template<class T>X<T>::X(int a, int b){ //constructor
    A = a;
    B = b;
    pData = new T*[A];
    for(int i=0;i<A;i++)
        pData[i]= new T[B];
    //Fill pData with something of type T
}
int main(){
    //...
    std::cout<<"Give the primitive type of the array"<<std::endl;
    std::cin>>type;
    if(type=="int"){
        X<int> XArray(a,b);
    } else if(type=="char"){
        X<char> Xarray(a,b);
    } else {
        std::cout<<"Not a valid primitive type!";
    } // can be many more if statements.
    Xarray.print() //this doesn't work, as Xarray is out of scope.
}

由于实例 Xarray 是在 if 语句中构造的,因此我无法在其他任何地方使用它。我试图在 if 语句之前创建一个指针,但由于此时指针的类型未知,所以我没有成功。

处理此类问题的正确方法是什么?

4

4 回答 4

4

这里的问题是,X<int>并且x<char>是完全不相关的类型。

它们都是同一个模板类的结果这一事实在这里无济于事。

我可以看到几种解决方案,但这取决于您真正需要什么。

例如,您可以使X<>实例派生自具有该方法的通用非模板基类print()(最终作为纯虚拟)。但在你这样做之前,请确保它在功能层面上有意义:应该使用继承,因为它有意义,而不仅仅是因为技术限制。如果你这样做,你可能也会想要一个虚拟析构函数。

您还可以将 a 绑定并存储std::function<void ()>到您要调用的方法,但确保对象仍然“活动”(它们不在您当前的代码中:当它们超出范围时,和在您之前被销毁X<int>X<char>实际调用print())。

最终的解决方案是制作一些与两者兼容的变体类型 X<int>X<char>boost ::variant<>可以在这里提供帮助)。然后,您可以编写一个访问者来实现print()每种类型的功能。

选择最后一个解决方案,它会变成这样:

typedef boost::variant<X<int>, X<char>> genericX;

class print_visitor : public boost::static_visitor<void>
{
public:
    template <typename SomeType>
    void operator()(const SomeType& x) const
    {
        // Your print implementation
        // x is your underlying instance, either X<char> or X<int>.
        // You may also make several non-templated overloads of
        // this operator if you want to provide different implementations.
    }
};

int main()
{
  boost::optional<genericX> my_x;

  if (type=="int") {
    my_x = X<int>(a,b);
  } else if(type=="char") {
    my_x = X<char>(a,b);
  }

  // This calls the appropriate print.
  if (my_x) {
    boost::apply_visitor(print_visitor(), *my_x)
  }
}

我们实际上缺乏给出明确答案的知识:如果您的类是“实体”,那么您可能应该去继承。如果它们更像“值类”,那么变体方式可能更适合。

于 2013-02-20T12:41:34.893 回答
1

如果您想使用不同的数组,无论它们的类型如何,单独的模板都无法帮助您。X<int>目前,和之间完全没有关系X<char>

如果您想将它们视为公共类型的两个子类型,则必须使用继承(和动态分配的变量)。例如,所有人都X<T>可以继承相同的基类,比如Printable,并且您可以将数据存储在 a 中unique_ptr<Printable>

unique_ptr<Printable> r;
if(type=="int"){
    r.reset(new X<int>(a,b));
} else if(type=="char"){        
    r.reset(new X<char>(a,b);
}
r->print();

但这可能不是最好的设计。

一个可能更好的解决方案是,而不是尝试在 if 之外工作,而是将所有工作移到 if 内部。在您的简单示例中,这可以通过复制调用 print 来完成,但这也不是很好。但是,按照这个想法,我们可以创建一个模板函数来完成这项工作:

template<class T>
void workWithType(int a, int b)
{
   X<T> Xarray(a, b);
   Xarray.print();
}

//...

if(type=="int"){
    workWithType<int>(a,b);
} else if(type=="char"){
    workWithType<char>(a,b);
} 
于 2013-02-20T12:50:11.460 回答
1

C++ 是一种静态类型语言,这意味着您必须在编译时知道对象的类型。在这种情况下,您基于用户输入构造的对象的类型,因此在运行时不可能知道类型。

解决此问题的最常见方法是使用动态多态性,其中函数通过使用后期绑定的公共接口调用。我们在 C++ 中使用虚函数来实现这一点。例如:

struct IPrintable {
   virtual void print() = 0;
};

template<class T>
class X : public IPrintable {
  // Same code as you showed above.
};

int main() {
  std::cout<<"Give the primitive type of the array"<<std::endl;
  std::cin>>type;

  std::unique_ptr<IPrintable> XArray;

  if(type=="int"){
      XArray.reset(new X<int>(a,b));
  } else if(type=="char"){
      XArray.reset(new X<char>(a,b));
  } else {
      std::cout<<"Not a valid primitive type!";
  } // can be many more if statements.

  Xarray->print() // this works now!
}

这解决了超出范围的问题,并允许您使用 XArray 变量的动态类型进行打印。虚拟功能是使这成为可能的秘诀。

于 2013-02-20T12:57:43.297 回答
1

与其尝试将模板放入其中,不如采取main与其他建议相反的方式...将代码移出main并移入需要处理单一类型的自己的(可能是模板化的)函数中:

template <typename T>
void generateAndPrint(int a, int b) {
   X<T> x(a,b);
   x.print();
}
int main() { ...
   if (type=="int") generateAndPrint<int>(a,b);
   else if (type=="char") generateAndPrint<char>(a,b);
   else ...
}
于 2013-02-20T13:13:47.617 回答