How can I store objects of differing types in a C++ container?

前端 未结 8 1557
一个人的身影
一个人的身影 2020-11-27 16:26

Is there a C++ container that I could use or build that can contain, say, int and string and double types? The problem I\'m facing is

相关标签:
8条回答
  • 2020-11-27 17:19

    What I have for this question is not what I hoped would work. By what I think that you would like, is a container that stores multiple value types, that you can access at will.

    However, as such, a container would have to specify what value it holds, so you could have a class with 500 data types in it, with a correlating constructor for each data type, however, that would be super memory inefficient.

    Here is my proposed suggestion, I have worked on for a day, And I hope it meets your criteria:

    #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;
        }
    }
    

    The "unipointer" class should be initialized with a pointer to any object type, and also the type of object. The class can return, through a function, your data, although it is not very safe, and could be called with the wrong type of data.

    This is just an example of what could work, I sure hope that you take inspiration from it.

    And, to answer your original question, you would set up a list, or vector with the following format:

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

    PS: I am a beginner with objects and templates, so this might be messy. Many apoligies.

    0 讨论(0)
  • 2020-11-27 17:23

    Well, the first question would be: Why do you think you need to store objects of different, totally unrelated types in the same container? That seems fishy to me.

    If I had the need, I'd look into boost::variant or boost::any.

    0 讨论(0)
提交回复
热议问题