【问题标题】:Heterogeneous containers in C++C++ 中的异构容器
【发布时间】:2011-12-09 22:27:58
【问题描述】:

我看到了这个漂亮的图形,它根据不同的数据要求对适合的 STL 容器进行分类,例如:

-- 固定尺寸与可变尺寸

-- 相同类型的数据与不同类型的数据

-- 已排序数据与未排序数据

-- 顺序访问与随机访问

http://plasmahh.projectiwear.org/cce_clean.svg

我注意到在那张图片中,C++ STL 没有容器

  1. 可变大小
  2. 异构(不同类型的数据)。

C++ 没有这方面的东西吗?

PS - 可以对容器的不同属性进行许多排列,而 STL 中也可能没有提供许多其他排列。

【问题讨论】:

  • 从图像中我可以看出许多容器,如vectorqueuestack 等,根据它们的颜色显示为可变大小。不过不确定。
  • 您可以使用指向抽象基类的指针向量,即使其他解决方案可能更好
  • 这张图不是关于 STL,而是关于 C++11 标准库。

标签: c++ stl containers heterogeneous


【解决方案1】:

一般来说,C++ 容器旨在使用模板保存单一类型的对象。如果您想要所有从一种类型派生的不同类型,您可以存储一个指针容器(我想您也可以有一个 void* 容器来指向任何东西......)例如std::vector.

如果您想要完全不相关的类型,您可以存储可以安全引用其他类型的对象,例如 boost::any。

http://www.boost.org/doc/libs/1_47_0/doc/html/any.html

boost 网站上的一些示例:

#include <list>
#include <boost/any.hpp>

using boost::any_cast;
typedef std::list<boost::any> many;

void append_int(many & values, int value)
{
    boost::any to_append = value;
    values.push_back(to_append);
}

void append_string(many & values, const std::string & value)
{
    values.push_back(value);
}

bool is_int(const boost::any & operand)
{
    return operand.type() == typeid(int);
}
bool is_char_ptr(const boost::any & operand)
{
    try
    {
        any_cast<const char *>(operand);
        return true;
    }
    catch(const boost::bad_any_cast &)
    {
        return false;
    }
}

boost::variant 类似,但您指定所有允许的类型,而不是允许容器中的任何类型。

http://www.boost.org/doc/libs/1_47_0/doc/html/variant.html

std::vector< boost::variant<unsigned, std::string> > vec;
vec.push_back( 44);
vec.push_back( "str" );
vec.push_back( SomthingElse(55, 65) ); //not allowed

【讨论】:

    【解决方案2】:

    标准库中的基本原则是“容器”是同质的; C++ 标准不认为像 std::pairstd::tuple 这样的东西是容器。 (我认为该图具有误导性,因为它确实将它们视为容器。)如果您需要异构容器,则必须使用 boost::variant 的容器或类似的东西。

    【讨论】:

      【解决方案3】:

      一个尚未被 Boost 接受的库。但是建议包含的内容是针对此的:

      http://rawgit.com/joaquintides/poly_collection/website/doc/html/index.html

      它提供了一个名为 any_collection 的好类,它允许通过 boost::type_erasure::any 拥有一个异构容器: http://rawgit.com/joaquintides/poly_collection/website/doc/html/poly_collection/tutorial.html#poly_collection.tutorial.basics.boost_any_collection

      否则在 C++17 中有简单的方法来实现: https://gieseanw.wordpress.com/2017/05/03/a-true-heterogeneous-container-in-c/

      引用上述文章的例子:

      namespace andyg{
      struct heterogeneous_container{
      private:
          template<class T>
          static std::unordered_map<const heterogeneous_container*, std::vector<T>> items;
      public:
          template <class T>
          void push_back(const T& _t)
          {
              items<T>[this].push_back(_t);
          }
      };
      
      // storage for our static members
      template<class T>
      std::unordered_map<const heterogeneous_container*, std::vector<T>> heterogeneous_container::items;
      } // andyg namespace
      

      然后很容易使用:

      andyg::heterogeneous_container c;
      c.push_back(1);
      c.push_back(2.f);
      c.push_back('c');
      struct LocalStruct{};
      c.push_back(LocalStruct{});
      

      作者说这是一个玩具实现,但我认为这是一种非常聪明的实现方式,并且比 poly_collection 或变体向量具有简单性优势。

      【讨论】:

      • poly_collection has been accepted 在您撰写此评论 10 天后提升。
      • 我不明白,静态变量不能只有一个模板参数incerted一次,还是有多个模板实例化?有人可以指出我可以更好地理解这类事情的地方吗?
      【解决方案4】:

      std::pairstd::tuple 几乎不是 C++ 容器……所以不,STL 中没有异构容器,因为没有必要将它们内置。

      有多种方法可以创建此类容器。我推荐的方法是:

      • 使用多态性
      • 使用变体类型

      对于多态,你可以查看Boost Pointer Container library。

      boost::ptr_vector<Base> vec;
      vec.push_back(new Derived);
      vec.push_back(new Derived2);
      

      它模仿了 STL 容器,但提供了面向多态性的功能:

      • Base&amp; 访问元素
      • 自动内存处理
      • 特定的复制行为(使用new_clone 方法)
      • 语法糖:给定boost::ptr_vector&lt;Base&gt;::iterator it;*itBase&amp;

      如果您的类型不相关,另一种可能性是使用Boost Variant。基本上,变体类似于:

      enum { Type1, Type2, ... } _type;
      union {
        SomeType1 _1;
        SomeType2 _2;
        ...
      } _u;
      

      当然,由于它是 boost,它提供了特定的保证,以确保您只能访问当前处于活动状态的联合成员,并取消了对具有在传统联合中不可用的构造函数/析构函数的类的限制。

      它还提供了类似static_visitor这样的功能,它相当于一个类型的开关,如果其中一个可能的状态没有被访问,它就会产生编译错误。

      【讨论】:

        【解决方案5】:

        固定大小的异构容器(如std::tuple 要求在编译时知道类型。如果您想制作可变大小的异构容器,只需制作一个std::vector&lt;std::tuple&lt;T1,T2,...,TN&gt;&gt;

        如果您想要一个异构容器,其中类型在编译时未知(无论是变量还是固定大小),您必须将指针(或智能指针)存储到编译时已知的基本类型,或者或者考虑像boost::any 的容器。 STL 不直接提供这样一个固定大小或可变大小的容器,并在运行时确定异构元素。

        【讨论】:

        • std::tuplestd::pair 与任何给定的 UDT 一样都是异构容器。比如struct foo { int i; float f; std::string s; };,一个异构容器。
        • +1,STL 就是关于组合的。无需预先定义所有可能的组合。
        • @dalle:不是真的,你可以通过元组的成员iterate
        • 我不确定这些是我的答案中的 cmets。我没有说 std 没有异构容器,我只是说它没有在编译时具有未知类型的容器。
        【解决方案6】:

        如果您存储的元素是boost::anyboost::variant,那么您可以间接存储异构数据。

        【讨论】:

          【解决方案7】:

          我会向您指出这个图书馆。它被实现为真正的异构容器 https://github.com/hosseinmoein/DataFrame 它不使用多态性并因此存储指针。它使用连续的内存存储,就像 std::vector 一样。

          你可以这样写代码

          typedef StdDataFrame<unsigned long> MyDataFrame;
          
          MyDataFrame                df;
          std::vector<int>           intvec = { 1, 2, 3, 4, 5 };
          std::vector<double>        dblvec = { 1.2345, 2.2345, 3.2345, 4.2345, 5.2345 };
          std::vector<double>        dblvec2 = { 0.998, 0.3456, 0.056, 0.15678, 0.00345,
                                                 0.923, 0.06743, 0.1 };
          std::vector<std::string>   strvec = { "Insight", "John Dow", "Alakazam",
                                                "Persian Prince", "Bugs Bunny" };
          std::vector<unsigned long> ulgvec = { 1UL, 2UL, 3UL, 4UL, 5UL, 8UL, 7UL, 6UL }
          std::vector<unsigned long> xulgvec = ulgvec;
          
          // This is only one way of loading data into a DataFrame instance. There are
          // many different ways of doing it. Please see the documentation,
          // or dataframe_tester.cc
          int rc = df.load_data(std::move(ulgvec),  // Index column
                                std::make_pair("int_col", intvec),
                                std::make_pair("dbl_col", dblvec),
                                std::make_pair("dbl_col_2", dblvec2),
                                std::make_pair("str_col", strvec),
                                std::make_pair("ul_col", xulgvec));
          

          【讨论】:

          • 您通常应该避免发布仅链接的答案。考虑添加一些 sn-ps 代码以及解释。如果您只想分享链接,则应将其添加为对原始问题的评论,而不是作为答案。
          猜你喜欢
          • 2015-02-24
          • 1970-01-01
          • 1970-01-01
          • 2021-03-17
          • 2018-08-21
          • 2023-04-01
          • 2020-05-24
          • 1970-01-01
          相关资源
          最近更新 更多