次のようなデータのさまざまな要件に基づいて、どのSTLコンテナが適しているかを分類するこの素晴らしいグラフィックを見ました。
-固定サイズと可変サイズ
-同じタイムと異なるタイプのデータ
-ソート済みデータと未ソートデータ
-順次アクセスとランダムアクセス
http://plasmahh.projectiwear.org/cce_clean.svg
その画像で、C++ STLにはコンテナがないことに気付きました
C++にはこれがないのですか?
PS-コンテナのさまざまなプロパティから作成された多くの順列があり、他の多くもSTLで提供されない場合があります。
一般的に、C++コンテナーは、テンプレートを使用して単一タイプのオブジェクトを保持するように設計されています。すべてが1つの型から派生した異なる型が必要な場合は、ポインターのコンテナーを格納できます(おそらく、void *のコンテナーを何にでも持つことができると思います...)。 std :: vector <MyBaseType *>。
まったく関係のない型が必要な場合は、boost :: anyなど、他の型を安全に参照できるオブジェクトを格納できます。
http://www.boost.org/doc/libs/1_47_0/doc/html/any.html
ブーストサイトの例:
#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
標準ライブラリの基本原則は、「コンテナ」は同種であるということです。 C++標準では、std::pair
やstd::Tuple
のようなものはコンテナーとは見なされません。 (グラフはコンテナーと見なされるため、誤解を招くと考えます。)異種コンテナーが必要な場合は、boost::variant
のコンテナー、またはそれらの線に沿ったものを使用する必要があります。
std::pair
とstd::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;
を指定すると、*it
はBase&
になりますタイプが無関係である場合、他の可能性は Boost Variant を使用することです。基本的に、バリアントは次のようになります。
enum { Type1, Type2, ... } _type;
union {
SomeType1 _1;
SomeType2 _2;
...
} _u;
もちろん、ブーストであるため、現在アクティブなユニオンのメンバーにのみアクセスできることを保証し、コンストラクター/デストラクタが従来のユニオンで使用できないクラスの制限を解除するための特定の保証を提供します。
また、static_visitor
のような機能も提供します。これは、タイプのスイッチと同等であり、考えられる状態の1つにアクセスしないと、コンパイルエラーが発生します。
Boostにまだ受け入れられていないライブラリ。しかし、インクルージョンのために提案されたものはこれに向けられています:
http://rawgit.com/joaquintides/poly_collection/website/doc/html/index.html
これは、any_collectionという名前のNiceクラスを提供します。これにより、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またはバリアントのベクトルよりも単純な利点があると思います。
固定サイズの異機種コンテナー(std::Tuple
など)では、コンパイル時に型がわかっている必要があります。可変サイズの異機種コンテナーを作成する場合は、std::vector<std::Tuple<T1,T2,...,TN>>
を作成します。
型がコンパイル時に不明である異種コンテナが必要な場合(それが可変サイズか固定サイズかに関係なく)、コンパイル時に既知の基本型へのポインター(またはスマートポインター)を格納するか、あるいは何かを検討する必要がありますboost::any
のコンテナのように。 STLは、実行時に決定される異種の要素を持つ固定サイズまたは可変サイズのこのようなコンテナーを直接提供しません。
保存する要素が boost::any
または boost::variant
すると、異種のデータを間接的に保存できます。
このライブラリを紹介します。これは、真の異種コンテナーであるように実装されています 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));