如何从if语句中获取类模板的实例?(C++)

How to get instance of class template out of the if statement? (C++)

本文关键字:实例 C++ if 语句 获取      更新时间:2023-10-16

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

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语句之前创建一个指针,但由于当时指针的类型未知,所以没有成功。

处理这种问题的正确方法是什么?

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

事实上,它们都是同一个模板化类的结果,这在这里没有帮助。

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

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

您也可以将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)
  }
}

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

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变量的动态类型进行打印。虚拟功能是实现这一目标的秘密。

与其尝试将模板放入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 ...
}

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

如果要将它们视为一个通用类型的两个子类型,则必须使用继承(以及动态分配的变量)。例如,所有X<T>都可以继承相同的基类,比如Printable,并且可以将数据存储在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之外工作。在您的简单示例中,这可以通过复制对打印的调用来完成,但这也不太好。但是,朝着这个想法,我们可以创建一个模板函数来完成这项工作:

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