C++ One std :: vector包含多种类型的模板类

jte*_*dit 40 c++ templates stdvector

我需要在一个向量中存储多种类型的模板类.

例如,对于:

template <typename T>
class templateClass{
     bool someFunction();
};
Run Code Online (Sandbox Code Playgroud)

我需要一个存储所有的向量:

templateClass<int> t1;
templateClass<char> t2;
templateClass<std::string> t3;
etc
Run Code Online (Sandbox Code Playgroud)

据我所知这是不可能的,如果有人可以说怎么样?

如果不可能有人解释如何进行以下工作?

作为一种解决方法,我尝试使用基本的非模板类并从中继承模板类.

 class templateInterface{
     virtual bool someFunction() = 0;
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction();
 };
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个向量来存储基础"templateInterface"类:

std::vector<templateInterface> v;
templateClass<int> t;
v.push_back(t);
Run Code Online (Sandbox Code Playgroud)

这产生了以下错误:

error: cannot allocate an object of abstract type 'templateInterface'
note: because the following virtual functions are pure within 'templateInterface'
note: virtual bool templateInterface::someFunction()
Run Code Online (Sandbox Code Playgroud)

为了解决这个错误,我在templateInterface中使函数不是一个纯虚拟的,通过提供一个函数体,这个编译但是在调用函数时不使用overide,而是在虚函数中使用body.

例如:

 class templateInterface{
     virtual bool someFunction() {return true;}
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction() {return false;}
 };

 std::vector<templateInterface> v;
 templateClass<int> i;
 v.push_back(i);
 v[0].someFunction(); //This returns true, and does not use the code in the 'templateClass' function body
Run Code Online (Sandbox Code Playgroud)

有没有办法解决这个问题,以便使用重写的函数,还是有另一种解决方法可以在一个向量中存储多个模板类型?

lee*_*mes 28

为什么你的代码不起作用:

上调用虚函数不使用多态.它调用的函数是为编译器看到的这个精确符号的类型定义的,而不是运行时类型.将子类型插入基类型的向量时,您的值将转换为基类型("类型切片"),这不是您想要的.对它们调用函数现在将调用为基类型定义的函数,因为它不是那种类型.

如何解决这个问题?

使用此代码段可以重现同样的问题:

templateInterface x = templateClass<int>(); // Type slicing takes place!
x.someFunction();  // -> templateInterface::someFunction() is called!
Run Code Online (Sandbox Code Playgroud)

多态性仅适用于指针引用类型.然后,它将使用指针/引用后面的对象的运行时类型来决定调用哪个实现(通过使用它的vtable).

对于类型切片,转换指针是完全"安全的".您的实际值根本不会被转换,多态性将按预期工作.

示例,类似于上面的代码片段:

templateInterface *x = new templateClass<int>();  // No type slicing takes place
x->someFunction();  // -> templateClass<int>::someFunction() is called!

delete x;  // Don't forget to destroy your objects.
Run Code Online (Sandbox Code Playgroud)

矢量怎么样?

因此,您必须在代码中采用这些更改.您可以简单地将指针存储到向量中的实际类型,而不是直接存储值.

使用指针时,您还必须关心删除已分配的对象.为此,您可以使用自动关注删除的智能指针.unique_ptr是一种这样的智能指针类型.它会在超出范围时删除指针("唯一所有权" - 范围是所有者).假设您的对象的生命周期绑定到范围,这是您应该使用的:

std::vector<std::unique_ptr<templateInterface>> v;

templateClass<int> *i = new templateClass<int>();    // create new object
v.push_back(std::unique_ptr<templateInterface>(i));  // put it in the vector

v.emplace_back(new templateClass<int>());   // "direct" alternative
Run Code Online (Sandbox Code Playgroud)

然后,使用以下语法在其中一个元素上调用虚函数:

v[0]->someFunction();
Run Code Online (Sandbox Code Playgroud)

确保将所有函数设置为虚拟的,这些函数应该可以被子类覆盖.否则,将不会调用它们被覆盖的版本.但是既然你已经引入了一个"接口",我相信你正在使用抽象函数.

替代方法:

做你想做的事情的另一种方法是在向量中使用变体类型.有一些变体类型的实现,Boost.Variant是一个非常受欢迎的类型.如果您没有类型层次结构(例如存储基元类型时),此方法尤其好用.然后你会使用像这样的矢量类型std::vector<boost::variant<int, char, bool>>