在地图中存储多种类型

时间:2016-08-31 19:07:50

标签: c++ templates stl containers std

我想在地图中存储多种类型的对象,我想出了这个解决方案。我必须知道每个对象的类型,所以我不能使用 boost :: any 。有没有更好的方法来做到这一点,或者这是一个可以接受的解决方案?

enum eType
{
    TYPE_STRING,
    TYPE_NUMBER,
};

class CType
{ 
public:
    int GetType() { return m_Type; }

protected:
    int m_Type;
};

template <typename T>
class CData : public CType
{
public:
    CData(const T & rData, int iType)
    {
        m_Type  = iType;
        m_Data  = rData;
    }

    T & GetData() { return m_Data; }

private:
    T m_Data;
};

std::map<unsigned long, CType *> map_Data;

void main()
{
    // Create a new data with TYPE_NUMBER
    CData<short> data(32767, TYPE_NUMBER);

    // Add it to the map
    map_Data[0] = &data;

    // Get the type
    switch (map_Data[0]->GetType())
    {
        case TYPE_NUMBER:
        {
            // Cast the first element to CData
            CData<short> * pField = (CData<short> *)map_Data[0];

            // Print the data
            printf("Data: %d\n", pField->GetData());
        }
        break;

        case TYPE_STRING:
        {
            // Cast the first element to CData
            CData<std::string> * pField = (CData<std::string> *)map_Data[0];

            // Print the data
            printf("Data: %s\n", pField->GetData().c_str());
        }
        break;
    }
}

3 个答案:

答案 0 :(得分:1)

使用boost::anyboost::any_cast

检查any是否为int;

bool is_int(const boost::any & operand)
{
    return operand.type() == typeid(int);
}

检查any是否为const char *

bool is_char_ptr(const boost::any & operand)
{
    try
    {
        any_cast<const char *>(operand);
        return true;
    }
    catch(const boost::bad_any_cast &)
    {
        return false;
    }
}

答案 1 :(得分:0)

如果您不想使用getType等函数污染您的类,一个基本且易于使用的解决方案是使用标记联合,如下例所示:

#include <map>

struct StructA { };
struct StructB { };

struct TaggedUnion {
    enum { A, B } tag;
    union {
        StructA a;
        StructB b;
    };
};

int main() {
    std::map<std::size_t, TaggedUnion> map;
    map[0] = TaggedUnion{ TaggedUnion::A, StructA{} };

    switch(map[0].tag) {
    case TaggedUnion::A:
        // do whatever you want with map[0].a;
        // ...
        break;
    case TaggedUnion::B:
        // do whatever you want with map[0].b
        // ...
        break;
    }
}

答案 2 :(得分:0)

你的问题很有趣,我不明白为什么这么沮丧。不幸的是,你想要做的事情在C ++中是不可能的(我想知道它在Java或C#中是否可行)

我再次提出了我认为是你问题核心的链接。

creating-an-interface-for-an-abstract-class-template-in-c++

除此之外,您的实施很有趣。我只是在这里做了一些返工,因为我认为你可以省去一些没有讨厌的开关盒的线路:

#include <iostream>
#include <string>
#include <sstream>
#include <List>

using namespace std;

struct CType
{
    int GetType() { return m_Type; }
    string GetStringVal() { return m_string_val; }
    enum eType {     TYPE_STRING,
                     TYPE_NUMBER };
protected:
    int m_Type;
    string m_string_val;
};

template <typename T>
class CData : public CType
{
public:
    CData(const T & rData):m_Data(rData)
    {
        stringstream strs;
        m_Type  = GetType();
        //Mingw bug
        //m_string_val = std::to_string(m_Data); //c++11
        strs << m_Data;
        m_string_val = strs.str();
    }

    T & GetData() { return m_Data; }

private:
    T m_Data;
    CType::eType GetType();
};

template<> CType::eType CData<int>::GetType() { return TYPE_NUMBER; };
template<> CType::eType CData<string>::GetType() { return TYPE_STRING; };
//More specialization here

int main()
{
    cout << "Hello world!" << endl;

    CData<int> cd_int(5);
    CData<string> cd_str("SO contribution");

    list<CType> my_list = { cd_int, cd_str };

    for ( auto & elem : my_list)
        cout << elem.GetStringVal() << endl;

    return 0;
}

结果很自然:

Hello world!

5

SO贡献

----- ADD-ON 2016-Sept-05 -----

另一种可能性是存储一个函数(带有“this”捕获的lambda)返回结果而不是结果本身。它避免在m_Data字段更改时执行更新

#include <iostream>
#include <string>
#include <sstream>
#include <List>
#include <functional>

using namespace std;

struct CType
{
    int GetType() { return m_Type; }
    string GetStringVal() { return m_string_func(); }
    enum eType {     TYPE_STRING,
                     TYPE_NUMBER };
protected:
    int m_Type;
    function<string()> m_string_func ;
};

template <typename T>
class CData : public CType
{
    public:
    CData(const T & rData):m_Data(rData)
    {
        m_Type  = GetType();
        m_string_func = [this](){   //MinGW bug,otherwise to_string()
                                    stringstream strs;
                                    strs << m_Data;
                                    return strs.str();};
    }

private:
    T m_Data;
    CType::eType GetType();
};

template<> CType::eType CData<int>::GetType() { return TYPE_NUMBER; };
template<> CType::eType CData<string>::GetType() { return TYPE_STRING; };

int main()
{
    cout << "Hello world!" << endl;

    CData<int> cd_int(5);
    CData<string> cd_str("SO contribution");

    list<CType> my_list = { cd_int, cd_str };

    for ( auto & elem : my_list)
        cout << elem.GetStringVal() << endl;

    return 0;
}