Как я могу надежно получить адрес объекта, когда оператор& перегружен?
рассмотрим следующую программу:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
как сделать clyde
' s адрес?
Я ищу решение, которое будет работать одинаково хорошо для всех типов объектов. Решение C++03 было бы неплохо, но меня тоже интересуют решения C++11. Если возможно, давайте избегать любого поведения, связанного с реализацией.
Я знаю о C++11's std::addressof
шаблон функции, но я не заинтересован в его использовании здесь: я хотел бы понять, как стандартная библиотека разработчик может реализовать этот шаблон функции.
5 ответов:
обновление: в C++11 можно использовать
std::addressof
вместоboost::addressof
.
давайте сначала скопируем код из Boost, минус компилятор работает вокруг битов:
template<class T> struct addr_impl_ref { T & v_; inline addr_impl_ref( T & v ): v_( v ) {} inline operator T& () const { return v_; } private: addr_impl_ref & operator=(const addr_impl_ref &); }; template<class T> struct addressof_impl { static inline T * f( T & v, long ) { return reinterpret_cast<T*>( &const_cast<char&>(reinterpret_cast<const volatile char &>(v))); } static inline T * f( T * v, int ) { return v; } }; template<class T> T * addressof( T & v ) { return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 ); }
что произойдет, если мы передаем ссылка на функцию ?
Примечание:
addressof
не может использоваться с указателем на функциюв C++, если
void func();
объявляется, тоfunc
является ссылкой на a функция не принимает аргумент и не возвращает результат. Эта ссылка на функцию может быть тривиально преобразована в указатель на функцию -- from@Konstantin
: согласно 13.3.3.2 какT &
иT *
неразличимы для функции. Первый-это преобразование идентичности, а второй-преобразование функции в указатель, оба из которых имеют ранг "точного соответствия" (13.3.3.1.1 таблица 9).The ссылка на функцию проходит через
addr_impl_ref
, есть двусмысленность в разрешение перегрузки для выбораf
, который решается благодаря фиктивному аргументу0
, который являетсяint
первый и может быть повышен доlong
(Целостное Преобразование).таким образом, мы просто возвращает указатель.
что произойдет, если мы передадим тип с оператором преобразования ?
если оператор преобразования дает
T*
тогда у нас есть двусмысленность: дляf(T&,long)
интегральное продвижение требуется для второй аргумент, в то время как дляf(T*,int)
оператор преобразования вызывается на первом (спасибо @litb)вот когда
наaddr_impl_ref
кайф. Стандарт C++ предписывает, что последовательность преобразования может содержать не более одного пользовательского преобразования. Путем обертывания типа вaddr_impl_ref
и заставляя уже использовать последовательность преобразования, мы "отключаем" любой оператор преобразования, с которым поставляется тип.f(T&,long)
перегрузка выбрана (и Интеграл Акция выполнена).начто происходит для любого другого типа ?
f(T&,long)
перегрузка выбрана, потому что там тип не соответствует
по существу, вы можете переинтерпретировать объект как ссылку на char, взять его адрес (не будет вызывать перегрузку) и вернуть указатель обратно к указателю вашего типа.
Импульс.AddressOf код делает именно это, просто принимая дополнительную заботу о
volatile
иconst
квалификация.
секрет
boost::addressof
и реализация, предоставленная @Luc Danton, опирается на магиюreinterpret_cast
; стандарт явно заявляет в §5.2.10 ¶10, чтовыражение lvalue типа
T1
можно привести к типу " ссылка наT2
" если выражение типа "указатель наT1
" может быть явно преобразован в тип "указатель наT2
" С использованиемreinterpret_cast
. То есть, ссылка castreinterpret_cast<T&>(x)
имеет такое же влияние как преобразования*reinterpret_cast<T*>(&x)
встроенный&
и*
операторы. Результатом является значение lvalue, которое ссылается на тот же объект, что и исходное значение lvalue, но с другим типом.Итак, это позволяет нам преобразовать произвольную ссылку на объект в
char &
(с квалификацией cv, если ссылка имеет квалификацию cv), потому что любой указатель может быть преобразован в a (возможно, CV-qualified)char *
. Теперь, когда у нас естьchar &
, перегрузка оператора на объекте отсутствует дольше актуально, и мы можем получить адрес со встроенным&
оператора.реализация boost добавляет несколько шагов для работы с CV-квалифицированными объектами: первый
reinterpret_cast
делаетсяconst volatile char &
, иначе простойchar &
бросок не будет работать дляconst
и/илиvolatile
ссылки (reinterpret_cast
удалитьconst
). Тогдаconst
иvolatile
удаляется сconst_cast
, адрес берется с&
, и заключительныйreinterpet_cast
к" правильному " типу относится сделанный.The
const_cast
нужно удалитьconst
/volatile
это можно было бы добавить к неконстантным / изменчивым ссылкам, но это не "вредит" тому, что былоconst
/volatile
ссылка на первом месте, потому что окончательнаяreinterpret_cast
добавит cv-квалификацию, если она была там на первом месте (reinterpret_cast
не удается удалитьconst
но можно добавить его).
что касается остального кодаaddressof.hpp
кажется, что большинство из них для решения проблем. Элементstatic inline T * f( T * v, int )
кажется, что он нужен только для компилятора Borland, но его присутствие вводит необходимостьaddr_impl_ref
, в противном случае типы указателей будут пойманы этой второй перегрузкой.
Edit: различные перегрузки имеют различную функцию, см. @Matthieu M. отличный ответ.Ну, я больше не уверен в этом; я должен дополнительно исследовать этот код, но теперь я готовлю ужин :), я буду взглянуть на него позже.
Я видел реализацию
addressof
этого:char* start = &reinterpret_cast<char&>(clyde); ghost* pointer_to_clyde = reinterpret_cast<ghost*>(start);
Не спрашивайте меня, как это соответствует!
посмотри boost:: addressof и его реализации.