2015-10-06 3 views
3

Я использовал шаблон ниже для привязки данных к произвольным объектам. Каждый объект T живет в Wrapper<T>, а дополнительные данные доступны с фиксированным смещением от T.Безопасный доступ к родительскому классу путем литья первого поля

template <class T> 
struct Wrapper 
{ 
    T core; 
    int id; 

    template <class ...Args> 
    Wrapper(int id, Args&&... args) 
     : id(id) 
     , core(std::forward<Args>(args)...) { } 
}; 

template <class T> 
Wrapper<T>& getWrapper(T& core) 
{ 
    return reinterpret_cast<Wrapper<T>&>(core); 
} 

void process(std::string& s) 
{ 
    std::cout << "'" << s << "' - id " 
     << getWrapper(s).id << "\n"; 
} 

int main() 
{ 
    auto *wrapper = new Wrapper<std::string>(188, "Costa Rica"); 

    process(wrapper->core); 
} 

Если мое понимание стандарта является правильным, это портативное при условии T имеет стандартный макет.

Мой вопрос - GCC конкретный. По объектной модели GCC я могу предположить, что это будет работать и тогда, когда T не является стандартным макетом?

ответ

0

Как я уверен, вы подозревали, что вы не можете этого сделать. Фокус с standard-layout classes заключается в том, что их можно безопасно отличать с помощью reinterpret_cast указателю на свой первый нестатический элемент данных и обратно. Вы делаете это в своем методе getWrapper().

Класс стандартного макета становится нестандартным, когда он набирает виртуальных членов или использует виртуальное наследование. Когда это произойдет, gcc помещает указатель vtable в нулевое значение смещения, поэтому вы можете больше не делать reinterpret_cast первому элементу данных.

В вашей реализации вы можете использовать std::is_standard_layout для защиты от неправильного использования вашего шаблона.

+0

Даже если T имеет таблицу vtable, почему это повлияет на reinterpret_cast? Важно то, что Wrapper имеет «стандартную» компоновку (нет vtable), независимо от макета T. Заметьте, я также предполагаю, что адрес T совпадает с его указателем vtable. –

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