2016-01-06 42 views
0

我知道你不能將成員指針轉換爲指向非成員的指針(例如,void*),但是你可以在指向成員的指針之間進行轉換同班?例如:對於某些類別C和類型TU,是否可以將T C::*轉換爲U C::*在成員指針間轉換

我希望能夠將字符串名稱映射到某些類的指針成員。例如,給定:

template<class ClassType> 
struct mbr_map_traits { 
    typedef std::string mbr_name_type; 
    typedef void* ClassType::*any_mbr_ptr; 
    typedef std::map<mbr_name_type,any_mbr_ptr> map_type; 
}; 

/** 
* A %mbr_map is used to map a string to an arbitrary pointer-to-member of some class. 
* @tparam ClassType The class whose members to map to. 
*/ 
template<class ClassType> 
struct mbr_map : mbr_map_traits<ClassType>::map_type { 
    typedef typename mbr_map_traits<ClassType>::mbr_name_type mbr_name_type; 

    /** 
    * Initalizes an entry in the map so as to mape \a name to a pointer-to-member. 
    * @param name The name to map. 
    * @param p The pointer-to-member to map to. 
    */ 
    template<typename MemberType> 
    void mbr_init(mbr_name_type const &name, MemberType ClassType::*p) { 
     typedef typename mbr_map_traits<ClassType>::any_mbr_ptr any_mbr_ptr; 
     (*this)[ name ] = reinterpret_cast<any_mbr_ptr>(p);  // IS THIS OK? 
    } 

    /** 
    * Sets the value of a class member by name. 
    * @param c  The class whose member to set. 
    * @param name The name of the class member to set. 
    * @param value The value to set the member to. 
    * @return true only if \a name exists in the map. 
    */ 
    template<typename MemberType> 
    bool mbr_set(ClassType &c, mbr_name_type const &name, MemberType const &value) { 
     typedef typename mbr_map<ClassType>::const_iterator const_iterator; 
     const_iterator const found = this->find(name); 
     if (found != this->end()) { 
      typedef MemberType ClassType::*mbr_ptr; 
      c.*reinterpret_cast<mbr_ptr>(found->second) = value; // IS THIS OK? 
      return true; 
     } 
     return false; 
    } 
}; 

和一些一次性初始化:

struct S { 
    std::string s; 
    int i; 
    bool b; 
}; 

void mbr_map_init(mbr_map<S> *m) { 
    m->mbr_init("string_mbr", &S::s); 
    m->mbr_init("int_mbr", &S::i); 
    m->mbr_init("bool_mbr", &S::b); 
} 

我可以這樣做:

using namespace std; 

int main() { 
    mbr_map<S> m; 
    mbr_map_init(&m); 

    S s; 

    m.mbr_set(s, "string_mbr", string("hello")); 
    m.mbr_set(s, "int_mbr", 42); 
    m.mbr_set(s, "bool_mbr", true); 

    cout << s.s << endl; 
    cout << s.i << endl; 
    cout << s.b << endl; 
    return 0; 
} 

,並打印我設置的值。但這是合法

(我想這樣做的原因是爲了從配置文件中讀取到結構體成員參數名稱和值映射。)

回答

1

只要您在使用指針到成員之前將其轉換回原始類型,就沒有問題。

http://en.cppreference.com/w/cpp/language/reinterpret_cast描述的reinterpret_cast的:

....這純粹是一個編譯器指令,它指示編譯器處理位的表達的序列(對象表示),就好像它有類型NEW_TYPE。

只有下列的轉換可以做到....

1)積分,枚舉,指針,或指針到構件類型的表達式可以被轉換成其自己的類型。 結果值與表達式的值相同。

所以投型串的指針到構件指針到成員類型OTHERTYPE,然後在稍後的點鑄造相同的指針到成員類型的OTHERTYPE回指針TO-字符串類型的成員是非常好的,不會改變指向成員的原始值。

0

一些C類和類型T和U,你能轉換a TC :: *UC :: *

簡短答案是肯定的。

長的答案當然是。無論什麼類型的數據指針,只是給你一個結構內實際數據的偏移量。類型信息不在指針中編碼 - 數據類型在實際指針值外部。

不用說,你的代碼有點多毛

+0

那麼你會怎麼做呢? –

+0

我不會相信一些隨機的用戶輸入來指定類型。您可以使用預處理器對名稱進行字符串化,並使用它將名稱與類型和字段關聯起來,這是在編譯時完成的。在運行時你只需要一些與名字相關的專用類型解析器。沒有重新解釋在任何地方施放,也沒有類型猜測mbr_set。但是如果你想保留你的地圖,至少檢查一下類型是否相同。例如,你可以使用boost :: any,或者std :: type_info – user3427419

+0

我的代碼按照寫法,對名字和類型進行了硬編碼。用戶輸入只來自conf文件,根據定義,它必須是用戶可配置的。 –

0

是的,這是合法的。在用於reinterpret_cast的描述中,語言規範說,

類型「指向類型T1的X的成員」的prvalue可以顯式轉換到不同類型的「指針的prvalue到類型Y的成員T2「,如果T1和T2都是函數類型或兩種對象類型。 ...這個轉換的結果是 沒有指定,除了[將一個指針指向另一個指針指向另一個指向成員的指針並且返回原始指針指向成員值]。

所以這是允許的,但可能會或可能不會做你期望的。