C ++中的异质容器
-
25-10-2019 - |
题
我看到了这张不错的图形,该图形分类了哪些STL容器会根据数据的不同要求,例如:
- 固定尺寸与变量大小
- 相同的Tyme与不同类型的数据
- 排序与未分类数据
- 顺序与随机访问
http://plasmah.projectiwear.org/cce_clean.svg
我注意到该图像,C ++ STL没有容器
- 可变大小
- 异源(不同类型的数据)。
C ++不为此吗?
PS-可能有许多排列的容器的不同属性,而STL也可能没有提供许多其他属性。
解决方案
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
其他提示
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.
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 aBase&
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.
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.
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.
If the element you store would be a boost::any
or boost::variant
then you can indirectly store heterogeneous data.