Подтвердить что ты не робот

Гетерогенные контейнеры в С++

Я увидел эту красивую графику, которая классифицирует, какой контейнер STL будет соответствовать на основе различных требований к данным, например:

- Фиксированный размер Vs Размер переменной

- Данные одного и того же типа Vs другого типа

- Отсортированные несваренные данные Vs

- случайный доступ с последовательным Vs

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

Я заметил на этом изображении, что в С++ STL нет контейнера, который

  • Переменный размер
  • Гетерогенные (данные разных типов).

У С++ нет чего-то для этого?

PS. В STL может быть много перестановок, из которых могут быть разные свойства контейнеров, и многие другие тоже могут не предоставляться.

4b9b3361

Ответ 1

В общем случае контейнеры С++ предназначены для хранения объектов одного типа с использованием шаблонов. Если вы хотите, чтобы разные типы, все производные от одного типа, вы могли хранить контейнер указателей (я думаю, вы могли бы также иметь контейнер 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

Ответ 2

Основным принципом в стандартной библиотеке является то, что "контейнеры" являются однородными; в стандарте С++ не рассматриваются такие вещи, как std::pair или std::tuple. (Я считаю, что график вводит в заблуждение, так как он рассматривает их как контейнеры.) Если вам нужен гетерогенный контейнер, вам нужно будет использовать контейнер boost::variant или что-то в этих строках.

Ответ 3

std::pair и std::tuple вряд ли являются контейнерами С++... так что нет, в 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;, *it является Base&

Если ваши типы не связаны друг с другом, другая возможность заключается в использовании Boost Variant. В принципе, вариант похож на:

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

Конечно, с тех пор, как он ускоряется, он предоставляет конкретные гарантии, чтобы убедиться, что вы можете получить доступ только к члену действующего в данный момент союза и лишить ограничение на классы с конструкторами/деструкторами, которые не могут использоваться в традиционных объединениях.

Он также предоставляет средства, такие как static_visitor, который является эквивалентом переключателя типа, и сделает ошибку компиляции, если одно из возможных состояний не посещено.

Ответ 4

Гетерогенные контейнеры фиксированного размера (например, std::tuple требуют, чтобы типы были известны во время компиляции. Если вы хотите создать гетерогенный контейнер с переменным размером, просто сделайте std::vector<std::tuple<T1,T2,...,TN>>.

Если вам нужен гетерогенный контейнер, где типы не известны во время компиляции (будь то переменный или фиксированный размер), вам придется хранить указатели (или интеллектуальные указатели) до базового типа, известного во время компиляции, или альтернативно рассмотрим что-то вроде контейнера boost::any. STL напрямую не обеспечивает такой контейнер как фиксированным, так и переменным размером с гетерогенными элементами, определенными во время выполнения.

Ответ 5

Библиотека, которая еще не принята в 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

В противном случае в С++ 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 или вектором вариантов.

Ответ 6

Если элемент, который вы храните, будет boost::any или boost::variant, то вы можете косвенно хранить неоднородные данные.