web-dev-qa-db-ja.com

異なるタイプのオブジェクトをC ++コンテナーに格納するにはどうすればよいですか?

たとえばintstringdouble型を含むことができる、使用またはビルドできるC++コンテナーはありますか?私が直面している問題は、たとえば、マップ、ベクトル、またはリストに、たとえば、次のように入力しようとするたびに、

int x;
string y;
double z;

私はフォーマットで制限されています:

list<int> mycountainer;
vector<string> mycontainer;

これにより、mycontainerは1つのタイプのみで構成されます。

誰かがジェネリックを提案する前に、C++に付属する標準のvectorおよびlistコンテナーすでにジェネリックなので、どちらも機能しません。ただし、複数のタイプを含めることはできません。

可能な限りBoostの使用も避けたいと思います。自分でコーディングできる簡単な方法がある場合は、Boostを使用したいと思います。

[編集]やあ、提案ありがとうございました-このコンテナの使い方を説明しましょう。少し複雑なので、上記の(大きな)単純化です。ここからの最良のオプションはブーストを使用することだと思います。再度、感謝します。

27
Ramsey

boost::anyを使用(または再実装)し、boost::anyのインスタンスをコンテナに格納できます。 boost::anyは、エッジのケースの多くと、一般的なケースでこの種の問題を解決するために必要な複雑さを扱ってきたため、これが最も安全です。

すばやくダーティなことをしたい場合は、構造体を作成するか、可能性のあるすべてのタイプのメンバーを含む共用体を作成し、列挙型またはオブジェクト内で「アクティブ」なタイプのその他のインジケーターを作成します。ユニオンにはいくつかの興味深いプロパティがあるため、特に注意が必要です(間違ったユニオンメンバーを読み取ると、未定義の動作を呼び出すなど、一度に「アクティブ」にできるメンバーは1つだけで、最後に書き込まれたメンバーです)。

でも、そのような構造が必要だとあなたが何をしているのか知りたいです。

22
Josh

さて、最初の質問は次のようになります:まったく異なるタイプのオブジェクトを同じコンテナに保存する必要があると思いますか?怪しいようです私に。

必要に応じて、boost::variantまたはboost::any

12
sbi

あなたが欲しいものは「ヘテロジニアスなコンテナ」と呼ばれています。 C++はSTLでそれらを技術的にサポートしていませんが、Boostはサポートしています。

それを考えると、この質問であなたの答えが見つかると思います: how-do-you-make-a-heterogeneous-boostmap

6
T.E.D.

構造体、クラス、またはstd :: pairのいずれかを使用できます。

[編集]

クラスと構造体の場合:

struct XYZ {
    int x;
    string y;
    double z;
};
std::vector<XYZ> container;

XYZ el;
el.x = 10;
el.y = "asd";
el.z = 1.123;
container.Push_back(el);

Std :: pairの場合:

#include <pair>
typedef std::pair<int, std::pair<string, double> > XYZ;
std::vector<XYZ> container;
container.Push_back(std::make_pair(10, std::make_pair("asd", 1111.222)));
3
ssmir

3つすべてを含む構造体を使用できます。

struct Data
{
    int intVal;
    std::string stringVal;
    double doubleVal;
};

次に、list mycontainer<Data>し、適切な値を使用します。ただし、値のタイプがわかっている必要があります。そうでない場合は、3つのデータ型のうちどれが使用されているかを示す追加フィールドを構造体に追加します。

struct Data
{
    enum DATATYPE { DT_INT, DT_STRING, DT_DOUBLE } type;

    int intVal;
    std::string stringVal;
    double doubleVal;
};

メモリの使用について心配している場合は、おそらくunionを使用できますが、私はそれらを使用しない傾向があります。それは私の側では不必要なパラノイアかもしれません。

1
James

もちろん、最も簡単な方法は、格納する各型のメンバーを持つ構造体またはクラスを定義することです。 ジョシュの答えBoost.Any を提案し、これはanythingをほぼ保持します。 restrict値をタイプintdouble、およびstd::stringの場合、 Boost.Variant の方が適切です。

Boostを使用したくないwant場合は、ハングアップを乗り越えて、とにかくそれを使用することをお勧めします。 「Not Invented Here」は自己破壊的なポリシーです。ただし、Boostを使用するできない場合は、代わりに独自のバリアントクラスを作成できます。 Andrei Alexandrescuは3部構成のシリーズ( part 1part 2part )を数年前に書いており、そのデザインに触発されましたブーストが使用するもの。

0
Rob Kennedy

この質問に対して私が持っているのは、私がうまくいくと思っていたものではありません。私があなたが望むと思うのは、自由にアクセスできる複数の値タイプを格納するコンテナです。

ただし、そのため、コンテナーは保持する値を指定する必要があるため、データ型ごとに相関コンストラクターを含む500のデータ型を含むクラスを作成できますが、これは非常に効率の悪いメモリになります。

これが私の提案された提案です、私は1日取り組んできました、そしてそれがあなたの基準を満たしていることを願っています:

#include <iostream>
#include <vector>

using namespace std;

enum class type: unsigned int {int_t, unsigned_int_t, string_t, double_t, float_t, bool_t, unipointer_t, vector_int_t, vector_unipointer_t};//just add item types here and in the switch statement to hold more void_ps in unipointer...

class unipointer {
    void* obj;//the pointer to the data. any type of pointer.
    type objtype;//the object type, kept as an enum class.
    struct void_p {//template magic... ;D
        void* void_ptr;
        template<typename T>//when object is initialized, it converts the the void* pointer to the output value.
        operator T() {
            return reinterpret_cast<T&>(void_ptr);
        }
        void_p(void* val): void_ptr(val) {};
    };
public:
    unipointer(void_p ptr, type ptrtype) : obj(ptr), objtype(ptrtype) {}

    type get_type(void) {//Once you call this function, you know the type of data stored, and can call other functions accordingly.
        return objtype;
    }
    template<typename T>//With a temlate, get any value through a pointer to it.
    T get_ptr(void){
        return reinterpret_cast<T&>(obj);
    }
    template<typename T>//With a temlate, get any value, as an object
    T get_object(void) {
        return *get_ptr<T*>();
    }
    void_p get_auto_pointer(void) {//get any pointer to value, can't be assigned to "auto*"!
        return unipointer::void_p(obj);
    }
    void_p get_auto_object(void) {//get any value, can't be assigned to "auto"!
        return *(void_p*)get_auto_pointer();
    }
};

void process_stuff(unipointer& thing, unsigned int num_of_tabs);

int main() {
    double initialization = 1.2345;
    float even_another = 3.14159f;
    unipointer items(new vector<unipointer>{//one thicc object instance
        //Initialization examles:
        unipointer(new int(-12345), type::int_t),
        unipointer(new unsigned int(4'294'967'295), type::unsigned_int_t),
        unipointer(new string("That is how I store my items."), type::string_t),
        unipointer(&initialization, type::double_t),
        unipointer(&even_another, type::float_t),
        unipointer(new bool(1), type::bool_t),
        unipointer(new unipointer(new unipointer(new unipointer(new string("OMG! NESTING!"), type::string_t), type::unipointer_t), type::unipointer_t), type::unipointer_t),
        unipointer(new vector<int>{ 1,2,3 }, type::vector_int_t),
        unipointer(new vector<unipointer>{
            unipointer(new string("That is how I store my nested items."), type::string_t),
            unipointer(new vector<int>{4,5,6}, type::vector_int_t),
            unipointer(new string("Is your head brimming with ideas yet?"), type::string_t)
        } , type::vector_unipointer_t)
    }, type::vector_unipointer_t);

    cout << "What is in the \"items\" unipointer:" << endl;
    process_stuff(items, 1);
    system("pause");
}

void process_stuff(unipointer& thing, unsigned int num_of_tabs) {
    //declare variables & lamda for interpretaion methods, using variable assignment with "get auto object/pointer"
    unsigned int* test = 0;
    double test_2 = 0;
    auto tab_to_current = [num_of_tabs]() {
        for (unsigned int i = 0; i < num_of_tabs; ++i) {
            cout << "\t";
        }
    };
    //format the thing.
    tab_to_current();
    //look through and do stuff
    switch (thing.get_type()) {//just add item types here and in the enum class to hold more void_ps in unipointer...
    case type::int_t:
        cout << "The integer: " << *thing.get_ptr<int*>() << "." << endl;//one way of getting object back from class
        break;
    case type::string_t:
        cout << "The string: \"" << thing.get_object<string>() << "\"." << endl;//another way
        break;
    case type::unsigned_int_t:
        test = thing.get_auto_pointer();//another way
        cout << "The unsigned integer: " << *test << "." << endl;//don't forget to de-reference it!
        delete test;
        break;
    case type::double_t:
        test_2 = thing.get_auto_object();
        cout << "The double: " << test_2 << "." << endl;//even another way!
        break;
    case type::float_t:
        cout << "The float: " << float(thing.get_auto_object()) << "." << endl;//even another way!
        break;
    case type::bool_t:
        cout << "The boolean: " << *(bool*)thing.get_auto_pointer() << "." << endl;//even another way!
        break;
    case type::unipointer_t:
        cout << "A unipointer, and in it:" << endl;
        process_stuff(*&thing.get_object<unipointer>(), num_of_tabs+1);
        tab_to_current();
        cout << "[End of unipointer]" << endl;
        break;
    case type::vector_int_t:
        cout << "A vector of integers, and in it:" << endl;
        for (unsigned int i = 0; i < thing.get_object<vector<int>>().size(); ++i) {
            tab_to_current();
            cout << "\tItem " << i << ": " << thing.get_object<vector<int>>().at(i) << endl;
        }
        tab_to_current();
        cout << "[End of vector of integers]" << endl;
        break;
    case type::vector_unipointer_t:
        cout << "A vector of unipointers, and in it:" << endl;
        for (unsigned int i = 0; i < thing.get_object<vector<unipointer>>().size(); ++i) {
            process_stuff(*&thing.get_object<vector<unipointer>>().at(i), num_of_tabs + 1);
        }
        tab_to_current();
        cout << "[End of unipointer vector]" << endl;
        break;
    }
}

「unipointer」クラスは、任意のオブジェクトタイプへのポインタとオブジェクトのタイプで初期化する必要があります。このクラスは、安全ではありませんが、関数を介してデータを返す可能性があり、誤ったタイプのデータで呼び出される可能性があります。

これは機能するもののほんの一例です。きっとそれからインスピレーションを得ていただければ幸いです。

そして、元の質問に答えるには、次の形式でリストまたはベクターを設定します。

vector/list:
|
|unipointer(*double)
|
|unipointer(*int)
|
|unipointer(*string)
|
...
|
end

PS:私はオブジェクトとテンプレートの初心者なので、これは面倒かもしれません。多くの政治。

0
Eric Petersen

保存する必要があるアイテムの数が限られている場合は、それらをクラスまたは構造体に配置します。

このコンテナに保存する必要のあるアイテムに制限がない場合は、オブジェクトとして保存し、独自のタイプにキャストすることしかできないため、別の方法で確認してください。それらにアクセスする必要があります。

ただし、アイテムがコンテナーに含まれる可能性がある場合、コンテナー内の特定のアイテムの種類を知る方法がないため、それらをキャストできません。

C++にリフレクションが含まれている場合、これを行う方法がある可能性がありますが、C++にはリフレクションがありません。

0
jcvandan