std:: карта указателей полиморфных переменных-членов

Я работаю над реализацией карты указателей переменных-членов, связанных со строковым ключом. Все переменные варьируются от базового класса "BaseA". При доступе к переменным с карты требуется только использование методов базового класса (getDesc () в образце), поэтому нет необходимости извлекать исходный тип.

Этот код компилируется и выполняется под GNU g ++ 6.2.1, но, согласно тому, что я прочитал, использование reinterpret_cast не переносимо и может не работать с другими компиляторами. Это правильно? Или этот код соответствует стандартам C ++? Есть ли другой способ сделать это без использования reinterpret_cast? Одно из требований заключается в том, что "Vars" должны быть копируемыми с реализациями копирования по умолчанию и назначениями копирования.

Образец кода:

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

1 ответ

Да, очень мало гарантий о том, что reinterpret_cast будет делать, и приведение от одного указателя к члену к другому не является одним из них (если только вы не приведете обратно к исходному типу, который на самом деле вам не поможет).

Безопасный и простой способ сделать это использует 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 + "]");
    }
};

Обратите внимание, что если вам никогда не понадобится vars словарь для изменения, вы можете превратить его в static const член. (Это будет означать, что вам нужно определить и инициализировать его вне класса в исходном файле.)

Другие вопросы по тегам