【问题标题】:Iterating through collection of different types in C++遍历 C++ 中不同类型的集合
【发布时间】:2011-07-28 16:42:45
【问题描述】:

情况

我有一个模板类TIppImage<T> 用于T 类型的图像。我有单例类CIppMemoryManager,它可以存储许多不同大小和类型的图像。

class CIppMemoryManager
{
public:
  /// ... Singleton interface ... 

  template<class T> TIppImage<T>* GetImage(width, height);

private:
  CIppMemoryManager();
  ~CIppMemoryManager();

  std::map<IppDataType, void*> m_Containers;
};

IppDataType 是枚举,其值对应于实际类型。所有管理都在模板类TIppImageContainer&lt;T&gt;中完成。并且这个类的所有特化都存储在m_Containers 中作为void*。这不是很好,但至少很简单。

使用这种方法,我可以像这样简单地实现模板GetImage 方法:

template<class T> TIppImage<T>* CIppMemoryManager::GetImage(width, height)
{
  return reinterpret_cast<TIppImageContainer<T>*>(m_Containers[
    TIppTypeTraits<T>::ipp_data_type])->GetImage(width, height);
}

我使用特征类 TIppTypeTraits&lt;T&gt; 从给定类型获取枚举值。

问题

我不能简单地实现像构造函数这样的非模板方法。我需要明确处理所有可能的类型:

CIppMemoryManager::CIppMemoryManager()
{
  m_Containers[ipp8u] = new CIppImageContainer<Ipp8u>;
  m_Containers[ipp8s] = new CIppImageContainer<Ipp8s>;
  m_Containers[ipp16u] = new CIppImageContainer<Ipp16u>;
  m_Containers[ipp16s] = new CIppImageContainer<Ipp16s>;
  ...
}

更糟糕的是,对于析构函数我还需要处理void*

CIppMemoryManager::~CIppMemoryManager()
{
  delete reinterpret_cast<TIppImageContainer<Ipp8u>*>(m_Containers[ipp8u]);
  delete reinterpret_cast<TIppImageContainer<Ipp8s>*>(m_Containers[ipp8s]);
  delete reinterpret_cast<TIppImageContainer<Ipp16u>*>(m_Containers[ipp16u]);
  delete reinterpret_cast<TIppImageContainer<Ipp16s>*>(m_Containers[ipp16s]);
  ...
}

所以,问题是:

a) 有什么方法可以遍历不同类型的集合吗?由于函数不是模板,因此无法在此处使用特征类。

b) 有没有更好的方法来存储容器集合 - 不同类型的对象?当它们只是普通模板类的不同特化时,容器本身就非常简单。

【问题讨论】:

    标签: c++ templates void-pointers typetraits typelist


    【解决方案1】:

    我认为 boost 库中的类变体 (boost::variant) 可能会对您有所帮助。您可以使用访问者根据存储在变体中的类型执行适当的代码。 std::vector&lt;boost::variant&lt;T0, T1,...&gt;&gt; 可以存储不同类型的对象列表。

    由于您的对象相似,它们在内存中的大小可能相同,这是一件好事,因为 boost::variant 存储是基于堆栈的(没有堆分配 - 这更快)。

    【讨论】:

    • 它将如何帮助迭代不同类型的集合?
    • 考虑一些variant&lt;int, float, double&gt; 和一个结构my_visitor,其中为变量中包含的每种类型定义了运算符()。如果xvariant&lt;int, float, double&gt; 的一个实例,那么boost::apply_visitor(my_visitor(), x) 将根据变量中存储的类型调用访问者的适当operator()。现在,如果你有一个std::vector&lt;variant&lt;int, float, double&gt;&gt;,你可以为每个元素使用apply_visitor
    • 说实话,难以置信。没有关于向量某些元素的编译时间的类型信息,因为这是运行时结构。它将如何推断出正确的过载?.. 或者我应该只执行boost::variant 吗? :)
    • 是的,它是运行时检测。 Variant 内部保存一个索引,指示存储的类型。您可以将 apply_visitor 视为 switch(index),其中每个案例都调用正确的重载方法。
    • 谢谢!我实际上不会在这个特定问题中使用它,但我认为在其他情况下可以方便地利用它。
    【解决方案2】:

    多态CIppImageContainer&lt;T&gt;(使它们都共享一个公共基类)和智能指针有什么问题?

    或者某种boost::variant

    【讨论】:

    • 似乎可以解决void*使用虚拟析构函数进行销毁的问题。只是没有考虑模板类的非模板祖先。但是,GetImage 不能是虚拟的,因为它使用模板参数。
    【解决方案3】:

    boost::mpl::for_each 是为这项工作量身定做的。定义一个类型的向量来操作,一个函子或 lambda 表达式来做某事,你就完成了。

    【讨论】:

      【解决方案4】:

      boost::variant 是最有可能的候选者,但有时variantS 会变得相当大,因为它们需要一些额外的存储空间并且还必须处理对齐问题。所以也许boost::any 在某些情况下也有优势:

      std::vector<std::pair< Type, boost::any > > data;
      

      轻松地迭代这样的容器更难(boost::transform_iterator 不能有多个返回类型,所以如果没有一些模板技巧,这将无法工作)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-23
        • 2020-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多