2

我正在实现与字符串关键字相关联的成员变量指针映射。 所有变量的范围从基类“BaseA” 从映射访问变量时,只需要使用基类方法(示例中的getDesc()),因此不需要检索原始类型。std ::多态成员变量指针映射

该代码编译并在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

种类无关,但我建议不要使用'std :: string'作为'std :: map'的键。如果可能的话(如在大多数情况下)使用'enum'。 – DeiDei

+0

谢谢DeiDei。我可以使用枚举,但在这种情况下,这个想法是,键是由用户输入的命令行,所以我明白一个字符串更适合 – SNJ

回答

2

是的,有very few guarantees什么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成员。 (这意味着你需要在源文件的类之外定义和初始化它。)

+0

你能解释一下它是如何工作的......怎么可能' &Vars :: a1'被转换为函数? – Phil1970

+0

@ Phil1970标准并没有说明如何,只是指向成员的指针是一个有效的['Callable'](http://en.cppreference.com/w/cpp/concept/Callable),并且调用它根据'arg'的类型执行'arg。* ptm'或'(* arg)。* ptm'。所以它类似于编写'[ptm](Vars&v) - > BaseA&{return v。* ptm; }(但可能更高效,因为特定于编译器的库*可以安全地使用像reinterpret_cast这样的技巧)。 – aschepler

+0

谢谢aschepler!这就是我要找的! – SNJ