一个std::vector,包含多种类型的模板类
C++ One std::vector containing template class of multiple types
我需要在一个vector中存储模板类的多个类型。
如:
template <typename T>
class templateClass{
bool someFunction();
};
我需要一个向量来存储所有的
templateClass<int> t1;
templateClass<char> t2;
templateClass<std::string> t3;
etc
据我所知这是不可能的,如果是,有人能说怎么做吗?
如果不可能,有人能解释一下如何使以下工作?
作为一种工作,我尝试使用一个基类,非模板类,并从它继承模板类。
class templateInterface{
virtual bool someFunction() = 0;
};
template <typename T>
class templateClass : public templateInterface{
bool someFunction();
};
然后创建了一个vector来存储基类"templateInterface":
std::vector<templateInterface> v;
templateClass<int> t;
v.push_back(t);
产生如下错误:
error: cannot allocate an object of abstract type 'templateInterface'
note: because the following virtual functions are pure within 'templateInterface'
note: virtual bool templateInterface::someFunction()
为了修复此错误,我通过提供函数体使templateInterface中的函数不是纯虚函数,这将编译,但在调用函数时不使用覆盖,而是使用虚函数中的函数体。
,
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
是否有任何方法可以解决这个问题,以便使用覆盖的函数,或者是否有另一种解决方案来存储多个模板类型在单个向量中?
为什么代码不工作:
在值上调用虚函数不使用多态性。它调用的函数是根据编译器看到的符号类型定义的,而不是运行时类型。当您将子类型插入基类型的向量中时,您的值将被转换为基类型("类型切片"),这不是您想要的。在它们上调用函数现在将调用为基类型定义的函数,因为没有是该类型的。
如何解决这个问题?
同样的问题可以用下面的代码片段重现:templateInterface x = templateClass<int>(); // Type slicing takes place!
x.someFunction(); // -> templateInterface::someFunction() is called!
多态性只作用于指针或引用类型。然后,它将使用指针/引用后面的对象的运行时类型来决定调用哪个实现(通过使用它的虚函数表)。
转换指针对于类型切片来说是完全"安全"的。您的实际值根本不会被转换,多态性将按预期工作。
示例,类似于上面的代码片段:
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.
向量呢?
因此,您必须在代码中采用这些更改。可以简单地在vector中存储指向实际类型的指针,而不是直接存储值。
使用指针时,还必须注意删除已分配的对象。为此,您可以使用智能指针来自动删除。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
然后,用以下语法在这些元素上调用虚函数:
v[0]->someFunction();
确保所有函数都是虚的,可以被子类覆盖。否则将不会调用它们的覆盖版本。但是既然你已经介绍了一个"接口",我相信你是在处理抽象函数。
替代方法:另一种方法是在vector中使用变体类型。有一些变体类型的实现,比如Boost。变体是一个很流行的词。如果您没有类型层次结构(例如,当您存储基本类型时),这种方法特别好。然后,您将使用像
std::vector<boost::variant<int, char, bool>>
这样的向量类型。你会需要非模板基础。除此之外,你需要做出决定容器中实际对象所在的位置。如果他们都是静态对象(具有足够的生命周期),只是使用a std::vector<TemplateInterface*>
,并插入v.push_back(&t1);
等,应该可以达到这个效果。否则,您可能希望支持克隆,并将克隆保存在向量:最好与Boost指针容器,但std::shared_ptr
也可以使用。
到目前为止给出的解决方案都很好,但是要注意,如果您在示例中返回的模板类型不是bool,那么这些解决方案都没有帮助,因为无法事先测量实值表槽。从设计的角度来看,使用面向模板的多态解决方案实际上是有限制的。
解决方案1
这个解决方案的灵感来自Sean Parent的c++调味演讲。我强烈建议大家去youtube上看看。我的解决方案简化了一点,关键是将对象存储在方法本身中。
只有一个方法
创建一个类来调用存储对象的方法。
struct object {
template <class T>
object(T t)
: someFunction([t = std::move(t)]() { return t.someFunction(); })
{ }
std::function<bool()> someFunction;
};
然后像这样使用
std::vector<object> v;
// Add classes that has 'bool someFunction()' method
v.emplace_back(someClass());
v.emplace_back(someOtherClass());
// Test our vector
for (auto& x : v)
std::cout << x.someFunction() << std::endl;
几种方法对于多个方法使用共享指针在方法之间共享对象
struct object {
template <class T>
object(T&& t) {
auto ptr = std::make_shared<std::remove_reference_t<T>>(std::forward<T>(t));
someFunction = [ptr]() { return ptr->someFunction(); };
someOtherFunction = [ptr](int x) { ptr->someOtherFunction(x); };
}
std::function<bool()> someFunction;
std::function<void(int)> someOtherFunction;
};
其他类型
基本类型(如int
, float
, const char*
)或类(std::string
等)可以用与object
类相同的方式包装,但行为不同。例如:
struct otherType {
template <class T>
otherType(T t)
: someFunction([t = std::move(t)]() {
// Return something different
return true;
})
{ }
std::function<bool()> someFunction;
};
所以现在可以添加没有someFunction
方法的类型。
v.emplace_back(otherType(17)); // Adding an int
v.emplace_back(otherType("test")); // A string
第2号方案
经过一番思考,我们基本上在第一个解决方案中所做的是创建可调用函数数组。那么,为什么不这样做呢?
// Example class with method we want to put in array
struct myclass {
void draw() const {
std::cout << "myclass" << std::endl;
}
};
// All other type's behaviour
template <class T>
void draw(const T& x) {
std::cout << typeid(T).name() << ": " << x << std::endl;
}
int main()
{
myclass x;
int y = 17;
std::vector<std::function<void()>> v;
v.emplace_back(std::bind(&myclass::draw, &x));
v.emplace_back(std::bind(draw<int>, y));
for (auto& fn : v)
fn();
}
结论解决方案no . 1绝对是一个有趣的方法,它不需要继承也不需要虚函数。并且可以用于其他需要存储模板参数以供以后使用的地方。
另一方面,解决方案2更简单,更灵活,可能是更好的选择。如果您正在寻找一个存储多种类型的容器,那么您应该探索流行的boost库中的boost变体
- C++ 在堆栈中包含多态属性的类对象存储
- 有没有办法一次声明相同类型的多个对象,并通过一个表达式立即使用相同的右值初始化它们?
- 即使我没有包含多个文件,C++中的多个定义错误
- 实现包含多个 QQuickPaintedItems 的 QQuickView 的滚动
- C++文件包含多行
- 如何存储包含多个空格的字符串 c++
- 如果代码包含多个复杂度循环,如何计算复杂度
- 将包含多个元素的字符串作为输入并转换为矢量 C++
- 无法构造具有给定数据类型的多映射
- 项目中包含多个相同的头文件:C与C++
- C++是否有一个容器,每个类型最多存储一个对象
- 当我在结构中包含多个数组时,我的程序会跳过一堆代码
- 如何检查一个字符串是否包含多个其他字符串?
- 带有自动initializer_list包含多个表达式
- std::bind to a std::variant 包含多个 std::函数类型
- 将包含多个类型变量的 PVOID 数组传递给 _beginthreadex()
- 强制转换包含多态类型的模板时的未定义行为
- 如何构造一个字节数组,其中包含多个数据类型值
- c++错误:在一个声明中包含多个类型
- 如果lambdas包含多个语句,那么不允许它推导返回类型有什么原因吗