简体   繁体   中英

std::map of polymorphic member variables pointers

I'm working on implementing a map of member variables pointers associated with a string key. All variables range from a base class "BaseA" When accessing the variables from the map, it is only required to use the base class methods (getDesc () in the sample) so it is not necessary to retrieve the original type.

This code compiles and runs under GNU g ++ 6.2.1, but according to what I read, the use of reinterpret_cast is not portable and may not work with other compilers. Is this correct? Or does this code comply with C ++ standards? Is there any other way to do this without using the reinterpret_cast? One requirement is that "Vars" must be copyable with the default copy-contructor and copy-assignment implementations.

Sample code:

#include <iostream>
#include <sstream>
#include <map>
#include <typeinfo>

using namespace std;

struct BaseA
{
    virtual string getDesc()  = 0;
};

struct A1 : BaseA
{
    string getDesc() override { return "This is A1"; }
};

struct A2 : BaseA
{
    string getDesc() override { return "This is A2"; }
};

struct Vars
{
    A1 a1;
    A2 a2;

    map< string, BaseA Vars::* > vars;

    Vars()
    {
        vars["A1_KEY"] = reinterpret_cast<BaseA Vars::*>(&Vars::a1);
        vars["A2_KEY"] = reinterpret_cast<BaseA Vars::*>(&Vars::a2);
    }

    BaseA& get( const string& key )
    {
        auto it = vars.find( key );
        if ( it != vars.end())
        {
            return this->*(it->second);
        }
        throw std::out_of_range( "Invalid variable key:[" + key + "]");
    }
};                                

int main()
{
    Vars v;

    cout << "a1 description :" << v.get("A1_KEY").getDesc() << endl;
    cout << "a2 description :" << v.get("A2_KEY").getDesc() << endl;

    return 0;
}

Yes, there are very few guarantees about what reinterpret_cast will do, and casting from one pointer to member to another is not one of them (unless you then cast back to the original type, which doesn't really help you).

The safe and easy way to do this one is using std::function :

struct Vars
{
    A1 a1;
    A2 a2;

    map< string, std::function<BaseA&(Vars&)> > vars;

    Vars()
    {
        vars["A1_KEY"] = &Vars::a1;
        vars["A2_KEY"] = &Vars::a2;
    }

    BaseA& get( const string& key )
    {
        auto it = vars.find( key );
        if ( it != vars.end())
        {
            return it->second(*this);
        }
        throw std::out_of_range( "Invalid variable key:[" + key + "]");
    }
};

Note that if you never need the vars dictionary to change, you could turn it into a static const member. (This would mean you need to define and initialize it outside the class in a source file.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM