2016-01-06 3 views
0

Я знаю, что вы не можете преобразовать указатель-член к указателю на не-член (например, void*), но можете ли вы преобразовать между указателями на член для тот же класс? Например: для некоторого класса C и типы T и U, можете ли вы преобразовать 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; 
} 

и печатает значения I набора. Но есть ли это legal?

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

ответ

1

До тех пор, пока вы используете бросок назад к исходному типу перед использованием указателя на элемент, проблем нет.

Из http://en.cppreference.com/w/cpp/language/reinterpret_cast описания reinterpret_cast:

.... Это чисто директива компилятора, который инструктирует компилятор обработать последовательность битов (представление объекта) выражения, как если бы он имел тип new_type.

только следующие преобразования можно сделать ....

1) выражение интеграла, перечисления, указатель или указатель на член типа может быть преобразован в его собственный тип. Результирующее значение совпадает с значением выражения.

Так бросить член указателя на строку типа указатель на член типа OtherType, а затем на более позднем этапе литья тот же указатель на член типа OtherType обратно указатель-to член типа string отлично подходит и не изменит исходное значение указателя на член.

0

для некоторого класса C и типов T и U, вы можете преобразовать a TC: * до UC: *?

Короткий ответ: да.

Долгий ответ, конечно. Указатели данных любого типа просто дают вам смещение фактических данных внутри структуры. Информация типа не закодирована в указателе - тип данных является внешним по отношению к фактическому значению указателя.

Излишне говорить, что ваш код немного волосатый.

+0

Итак, как бы вы это сделали? –

+0

Я бы не стал доверять случайным пользовательским вводам для указания типа. Вы можете форматировать имена с помощью препроцессора и использовать его для связывания имен с типами и полями, которые выполняются во время компиляции. Во время выполнения у вас есть только специализированный синтаксический анализатор, связанный с именем. Никакой реинтерпрет не бросается нигде, и никакого типа угадывания mbr_set. Но если вы хотите сохранить свою карту, по крайней мере, проверьте, что тип тот же. Например, вы можете использовать boost :: any для этого или std :: type_info – user3427419

+0

Мой код, как написано, жестко кодирует имена и типы. Вход пользователя поступает только из файлов conf, которые по определению должны настраиваться пользователем. –

0

Да, это законно.В описании reinterpret_cast, язык спецификация говорит,

prvalue типа «указатель на член X типа T1» может быть явно преобразована в prvalue другого «типа указателя на член Y типа T2 ", если T1 и T2 - оба типа функций или оба типа объектов. ... результат этого преобразования является неуказанным, за исключением того, что [преобразование указателя в член к другому указателю на элемент и обратно дает исходный указатель на значение члена].

Так что это разрешено, но может или не может делать то, что вы ожидаете от него.

 Смежные вопросы

  • Нет связанных вопросов^_^