【问题标题】:C++ storing template structuresC++ 存储模板结构
【发布时间】:2012-08-28 08:14:48
【问题描述】:

我有一个基础结构FooBase:

struct FooBase { };

然后我创建了一个模板结构Foo,它是FooBase 的子结构:

template <typename typeName> struct Foo : public FooBase { typeName* foo };

在某个类中,我创建了一个FooBase 的向量并在其中添加了Foo 的实例:

vector <FooBase> FooVector
...    
Foo <Bar> fooInstance;
fooInstance.foo = new Bar();
FooVector.push_back ( fooInstance );

然后我需要访问存储的数据,但我收到了关于 FooBase 中成员 foo 缺席的可预测且明显的错误

FooVector[0].foo

我不会写类似的东西

Foo <Bar> fooInstance = FooVector[0]

因为我不知道模板参数。

如何将Foo 的实例存储在向量中,以便以后访问它们。 请注意,我不知道最后一步的模板参数 - 从向量中读取数据时。

附注不允许提升!

【问题讨论】:

  • 你可以通过typename 引用Bar 吗?你需要对Bar 做任何特定于它的类型的事情吗(我的猜测是没有)。
  • 您正在寻找的是一种称为类型擦除的技术。

标签: c++ class templates struct storage


【解决方案1】:

这里发生的事情,是在你的

FooVector.push_back ( fooInstance );

行,C++ 静默调用FooBase 的复制构造函数,因为您只能在向量中保留该类型的对象。由于Foo 公开继承自FooBase 方法FooBase::FooBase(FooBase const&amp;) 可以使用Foo 类型的对象调用。

所以,您实际上并没有存储Foos,而是实际上存储了FooBases。要做你想做的事,你需要一个std::vector&lt;FooBase*&gt;std::vector&lt;std::shared_ptr&lt;FooBase&gt; &gt;

但是,您的向量的内容仍然缺少foo 成员,因为静态类型仍然不是Foo。为了解决这个问题,您有一些选择。您可以dynamic_caststatic_cast 您的FooBase* 转换为Foo*,然后访问其foo 成员。但这可能会中断,因为 FooBase* 指针实际上可能持有不同于 Foo 的其他类型。

您为什么不直接使用std::vector&lt;Foo&lt;Bar&gt; &gt;

【讨论】:

  • Why don't you just use an std::vector&lt;Foo&lt;Bar&gt; &gt; instead? 因为不知道模板参数!酒吧只是一个例子。
  • @Kolyunya:那你怎么能指望.foo给你正确的类型呢?
  • 那是我的问题。如何使用我不知道的模板参数存储模板结构的向量。
  • @Kolyunya:这对我来说没有意义。你为什么要尝试提取一个你甚至不知道其类型的对象?也许你可以澄清你的问题。就目前而言,我不明白 - 显然没有其他人。
  • @Kolyunya:要访问X 类型的成员,您需要知道X 的完整类型。没有办法解决它。如果X 恰好是模板Y&lt;Z&gt;,那么您必须知道哪个ZY 一起使用。 C++ 应该如何知道.foo 应该是什么意思?
【解决方案2】:

你在这里切片:

vector <FooBase> FooVector
...    
Foo <Bar> fooInstance;
fooInstance.foo = new Bar();
FooVector.push_back ( fooInstance );

您正在将Foo&lt;Bar&gt; 推入FooBase 的向量中,因此您只存储了一个FooBase 对象。这个

FooVector[0]

返回对FooBase 的引用,它对Foo&lt;Bar&gt; 一无所知。你可以直接存储Foo&lt;Bar&gt;

vector<Foo<Bar>> FooVector;

或存储指向FooBase 的指针或智能指针,但无论如何您都必须将元素动态转换为Foo&lt;Bar&gt;*,这不是一个很好的解决方案。

【讨论】:

  • 是的,我知道。如何存储Foo 的向量?如果我将指向 Foo 的指针放在指向 FooBase 的指针的向量中,它会改变什么吗?
  • @Kolyunya:指针向量是避免切片的一种方法。但是,它会使向量的复制构造函数和复制赋值做错事。这是最丑陋的 C++ 问题之一,IMO。 (其他丑陋的解决方案包括 ptr_vector、boost::any 和智能指针向量。)
  • @Kolyunya 我在答案中添加了一些选项。
  • but then you will have to dynamic_cast the elements to Foo&lt;Bar&gt;* anyway, which isn't a very nice solution. 我不能投,因为我不知道模板参数的值。 Bar 只是一个例子
  • @Kolyunya:那你可以用返回的对象做什么?!如果你不知道它的类型,你怎么知道它支持哪些功能或者它有哪些数据成员?听起来您需要将需要调用的任何功能推送到您不知道其完整类型的对象到基类中。
【解决方案3】:

如前面的答案所述,您将 Bar 切片为 FooBase 对象。您的向量必须存储对 Foo 对象的引用,而不是对象本身。

最简单的方法是使用 boost 库中的 smart_ptr 类型之一。

#include <boost/shared_ptr.hpp>
#include <vector>
typedef std::vector< boost::shared_ptr<FooBase> > FooVector;
...
FooVector v;
v.push_back( new Bar() );

如果您将指针本身存储到向量中,您将遇到内存管理问题,使用 boost shared_ptr 类型将自动为您处理分配。

【讨论】:

  • (当然,您也可以为任何其他库使用智能指针类型或编写自己的,因为您不想使用 Boost。关键是您需要的语义,而不是实现.)
  • 正如大卫所说,你不必使用 boost;但是你应该为你的向量引用使用某种智能指针实现。而且你必须在你的向量中使用引用类型来避免切片问题。不使用智能指针会给您带来处理释放的负担;这使得使用一些向量方法(即擦除)非常危险。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 2017-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多