C++中的异构容器

pkmbmrz7  于 2023-05-02  发布在  其他
关注(0)|答案(8)|浏览(185)

我看到了这个漂亮的图形,它根据不同的数据要求分类了适合哪个STL容器,例如:
--固定大小VS可变大小
--同类型数据VS不同类型数据
--排序数据与未排序数据
--顺序访问VS随机访问
http://plasmahh.projectiwear.org/cce_clean.svg
我注意到在那个图像中,C++ STL没有容器
1.可变尺寸
1.异构(不同类型的数据)。
难道C++没有这方面的东西吗?
PS -容器的不同属性可以有许多排列,STL中可能也没有提供许多其他属性。

yqyhoc1h

yqyhoc1h1#

一般来说,C++容器被设计成使用模板来保存单一类型的对象。如果你想要不同的类型都是从一个类型派生的,你可以存储一个指针容器(我猜你也可以有一个void* 到任何东西的容器。..)例如std::vector〈MyBaseType*〉。
如果你想要完全不相关的类型,你可以存储可以安全地引用其他类型的对象,比如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
pcrecxhr

pcrecxhr2#

标准库中的基本原则是“容器”是同质的;C++标准不认为std::pairstd::tuple是容器。(我认为这张图有误导性,因为它确实把它们看作容器。)如果您需要一个异构容器,您必须使用boost::variant容器,或者沿着的容器。

bmvo0sr5

bmvo0sr53#

尚未被Boost接受的库。但建议纳入的目标是:
http://rawgit.com/joaquintides/poly_collection/website/doc/html/index.html
它提供了一个名为any_collection的好类,它允许通过boost::type_erasure::any拥有一个异构容器: www.example.com
否则在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或变量向量更简单。

atmip9wb

atmip9wb4#

std::pairstd::tuple几乎不是C++容器。...所以没有,STL中没有异构容器,因为没有必要内置它们。
有几种方法来创建这样的容器。我推荐的方法是:

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

对于多态性,您可以检查Boost Pointer Container库。

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

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

  • 访问元素为Base&
  • 自动存储器处理
  • 特定复制行为(使用new_clone方法)
  • 句法糖:给定boost::ptr_vector<Base>::iterator it;*itBase&

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

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

当然,由于它是boost,它提供了特定的保证,以确保您只能访问当前活动的联合体成员,并取消了对传统联合体中不可用的构造函数/析构函数的类的限制。
它还提供了一些工具,如static_visitor,它相当于类型上的开关,如果一个可能的状态没有被访问,它将使编译错误消失。

wh6knrhe

wh6knrhe5#

固定大小的异构容器(如std::tuple)需要在编译时知道类型。如果你想做一个可变大小的异构容器,只需要做一个std::vector<std::tuple<T1,T2,...,TN>>
如果你想要一个异构的容器,其中类型在编译时是未知的(无论是可变的还是固定大小的),你必须存储指向编译时已知的基类型的指针(或智能指针),或者考虑类似boost::any的容器。STL不直接提供这样的容器,无论是固定的还是可变大小的,在运行时确定的异构元素。

ukqbszuj

ukqbszuj6#

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

kxeu7u2r

kxeu7u2r7#

我会告诉你这个图书馆。它被实现为真正的异构容器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));
kulphzqa

kulphzqa8#

注意所有提到boost::variant的答案。
但是从C++17开始,标准本身提供了std::variant。这是一个类型安全的联合。

//A vector holds multiple types as declared in std::variant.
    std::vector<std::variant<int, double, std::string>> multiTypeVector;

    //Add eleemnts.
    multiTypeVector.push_back(8);
    multiTypeVector.push_back(8.0);
    multiTypeVector.push_back("test");
    multiTypeVector.push_back("test more");

    //Testing :: print elements.
    for (auto element : multiTypeVector)
        std::visit([](auto arg) {std::cout << arg << std::endl; }, element);

相关问题