2012-08-27 3 views
7

Я хотел бы получить смещение стандартной переменной элемента компоновки, если она снабжена указателем на эту переменную. Я не могу использовать offsetof, так как у меня есть указатель, а не имя. Текущий код, который у меня есть, выглядит примерно так, и мне интересно, есть ли стандарт, совместимый с тем, чтобы избавиться от переменной dummy.Смещение от указателя-члена без временного экземпляра

template<class T> 
struct { 
    ptrdiff_t get_offset(int (T::*mem)) 
    { 
    T dummy; 
    return reinterpret_cast<char*>(&(dummy.*mem)) 
     - reinterpret_cast<char*>(&dummy); 
    } 
}; 

Эта функция должна быть вызываемым с переменными точками int только членов (это намеренно).

Я вполне уверен, что компилятор фактически не создает переменную dummy, но все равно было бы неплохо, если бы я мог избавиться от нее. Я не могу использовать нулевой указатель, так как разворот null не определен (хотя он, вероятно, работает на всех распространенных компиляторах). Решение C++ 03 было бы хорошо, или решение C++ 11 также представляет интерес (но не может использоваться мной сейчас).

ПРИМЕЧАНИЕ: Я уже знаю, что это только стандарты, совместимые с T, является стандартным типом макета.

+0

Тип возврата должен быть 'ptrdiff_t', я полагаю, и вы должны использовать' std :: distance'. И функция должна быть 'static'. –

+0

@KerrekSB, да. Я компилирую с полными/дополнительными предупреждениями в GCC, но я думаю 'size_t == ptrdiff_t', поэтому никаких предупреждений. –

+0

'ptrdiff_t' подписан ... в любом случае,' фиктивный' также должен быть статическим, я полагаю. –

ответ

2

Я боюсь, что не существует стандартного решения, удовлетворяющего требованиям OP.

Я могу дать пару несоответствующих.

template<class T> 
    size_t get_offset(int (T::*mem)) 
    { 
    return reinterpret_cast<char*>(&(((T*)nullptr)->*mem))-reinterpret_cast<char*>(nullptr); 
    } 

Это забавно, но следующие работы в VC2010, используя offsetof быть макрос.

template<class T> 
    size_t get_offset(int (T::*mem)) 
    { 
    return offsetof(T, *mem); 
    } 
+0

Вы разыскиваете нулевой указатель через '-> *' оператор. Рассмотрим эквивалент '(* (T *) nullptr). * Mem' –

+0

Вы по-прежнему разыскиваете нулевой указатель. Просто потому, что 'offsetof' делает это, это не значит, что это не UB. –

+0

@PeterAlexander: Да, я знаю ... Боюсь, что не существует решения, удовлетворяющего требованиям OP. Нужна фиктивная переменная или разыменование NULL. – Andrey

7

Как насчет:

template<class T> 
struct { 
    ptrdiff_t get_offset(int (T::*mem)) 
    { 
    union { 
     int used; 
     T unused; 
    } dummy; 
    return reinterpret_cast<char*>(&(dummy.unused.*mem)) 
     - reinterpret_cast<char*>(&dummy.unused); 
    } 
}; 

Адрес члена союза не зависит от члена профсоюза строится. Работает уже на C++ 03, но только для POD.

+0

Так что эта версия базово устраняет любой вызов 'T :: T()' и 'T: ~ T()'. У него все еще есть манекен, но абстрактная машина выполняет меньше кода. –

+0

Ну, очевидно, вам нужна память, иначе вы не сможете сформировать указатели. – MSalters

+0

Это все еще UB, поскольку вы используете компонент «union», который не был активен в момент использования. Таким образом, как это лучше, чем 'reinterpret_cast (nullptr)'? Хуже того, что вы создаете кучу столовой комнаты для 'T'. Может существовать система, на которой это работает, но 'nullptr' не работает, но наоборот может быть правдой. – Yakk

1

Так как насчет:

template<class T> 
struct { 
    ptrdiff_t get_offset(int (T::*mem)) 
    { 
     return 
     (&reinterpret_cast<const char&>( 
      reinterpret_cast<const T*>(1)->*mem) 
      - reinterpret_cast<const char*>(1)  ); 
    } 
}; 

..?

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

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