C++ 中的异构容器

发布于 2024-12-10 15:30:16 字数 434 浏览 2 评论 0原文

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

-- 固定大小 Vs 可变大小

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

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

-- 顺序访问与随机访问

< a href="http://plasmahh.projectiwear.org/cce_clean.svg" rel="noreferrer">http://plasmahh.projectiwear.org/cce_clean.svg

我注意到那个图像,C++ STL 没有

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

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

PS - 容器的不同属性可能有多种排列,而 STL 中也可能未提供许多其他排列。

I saw this nice graphic which classifies which STL container would suit based on different requirements of data such as:

-- Fixed Size Vs Variable size

-- Data of same tyme Vs different type

-- Sorted Vs unsorted data

-- Sequential Vs random access

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

I notice in that image, that C++ STL there is no container which is

  1. Variable Size
  2. Heterogenous (data of different types).

Doesn't C++ have something for this?

PS - There can be many permutations made out the different properties of the containers and many others too might not be provided in STL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

oО清风挽发oО 2024-12-17 15:30:16

一般来说,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

Well generally C++ Containers are designed to hold objects of a single type using templates. If you want different types that are all derived from one type you can store a container of pointers (I guess you could also have a container of void* to anything...) e.g. std::vector<MyBaseType*>.

If you want completely unrelated types, you can store objects that can safely reference those other types, such as boost::any.

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

Some examples off the boost site:

#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 is similar, but you specify all the allowed types, rather than allowing any type in your container.

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
陌伤ぢ 2024-12-17 15:30:16

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

The basic principle in the standard library is that "containers" are homogeneous; the C++ standard doesn't consider things like std::pair or std::tuple to be containers. (I'd consider the graph misleading, since it does consider them as containers.) If you need a heterogeneous container, you'd have to use a container of boost::variant, or something along those lines.

鼻尖触碰 2024-12-17 15:30:16

尚未被 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 或变体向量具有简单性优势。

A library which is not yet accepted into Boost. But which was proposed for inclusion is targeted toward this :

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

It provides a nice class named any_collection which allows one to have an heterogeneous container via boost::type_erasure::any :
http://rawgit.com/joaquintides/poly_collection/website/doc/html/poly_collection/tutorial.html#poly_collection.tutorial.basics.boost_any_collection

Otherwise in C++17 there are simple way to implement this :
https://gieseanw.wordpress.com/2017/05/03/a-true-heterogeneous-container-in-c/

Quoting the example of the aforementioned article :

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

Then usable easily :

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

The author states it's a toy implementation, but I think this is a really clever way to implement it, and has a simplicity advantage over poly_collection or a vector of variants.

一花一树开 2024-12-17 15:30:16

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::iterator it;, *it 是一个 Base&

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

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

当然,由于它是 boost,它提供了特定的保证,以确保您只能访问当前活动的联合成员,并解除了对构造函数/析构函数不能在以下情况下使用的类的限制:传统工会。

它还提供了诸如static_visitor之类的功能,它相当于类型上的开关,并且如果未访问其中一种可能的状态,则会产生编译错误。

std::pair and std::tuple are hardly C++ containers.... so no, there is no heterogeneous containers in the STL, because it's not necessary to have them built-in.

There are several approaches to create such containers. The approaches I would recommend are:

  • using polymorphism
  • using a variant type

For Polymorphism, you can check Boost Pointer Container library.

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

It mimicks the STL containers, but provides functionalities geared toward polymorphism:

  • Access elements as Base&
  • Automatic memory handling
  • Specific copy behavior (using new_clone methods)
  • Syntactic sugar: given boost::ptr_vector<Base>::iterator it;, *it is a Base&

If your types are unrelated, the other possibility is to use Boost Variant. Basically, a variant is similar to:

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

Of course, since it's boost, it provides specific guarantees to make sure that you can only access the member of the union that is currently active and lifts the restriction on classes with constructors / destructors not being usable in traditional unions.

It also provides facilities, like the static_visitor, which is the equivalent of a switch on the type, and will make the compilation error out if one of the possible states is not visited.

π浅易 2024-12-17 15:30:16

固定大小的异构容器(如 std::tuple )要求在编译时知道类型。如果您想创建一个可变大小的异构容器,只需创建一个 std::vectorstd::tuple 即可。 std::tuple>

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

The fixed size heterogenous containers (like std::tuple require the types to be known at compile time. If you want to make a variable sized heterogeneous container, just make a std::vector<std::tuple<T1,T2,...,TN>>.

If you want a heterogeneous container where the types is not known at compile time (whether that would be variable or fixed sized) you'll have to store pointers (or smart pointers) to a base type known at compile time, or alternatively consider something like a container of boost::any. The STL doesn't directly provide such a container in either fixed or variable sized with heterogeneous elements determined at run time.

猫瑾少女 2024-12-17 15:30:16

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

If the element you store would be a boost::any or boost::variant then you can indirectly store heterogeneous data.

陪你到最终 2024-12-17 15:30:16

我会向您推荐这个图书馆。它被实现为真正的异构容器
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));

I would point you to this library. It is implemented to be true heterogeneous container
https://github.com/hosseinmoein/DataFrame
It does not use polymorphism and consequently store pointers. It uses continuous memory storage, just like a std::vector.

You can write code like this

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));
寂寞美少年 2024-12-17 15:30:16

注意到所有提到 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);

Noted all the answers mentioning about boost::variant.

But from C++17 the standard itself provides std::variant. It is a type-safe union.

    //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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文