Обновление: в 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
является ссылкой на функцию, не имеющую аргументов и не возвращающую результата.Эта ссылка на функцию может быть легко преобразована в указатель на функцию - из @Konstantin
: согласно 13.3.3.2 и T &
, и T *
неразличимы для функций.Первый - преобразование идентификаторов, а второй - преобразование функций в указатели, оба из которых имеют ранг «Точное совпадение» (13.3.3.1.1, таблица 9).
Ссылка на функцию пройти через 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)
(и Integral Promotionвыполняется).
Что происходит с любым другим типом?
Таким образом, перегрузка f(T&,long)
выбрана, поскольку тип не соответствует параметру T*
.
Примечание: из замечаний в файле относительно совместимости с Borland массивы не распадаются на указатели, а передаются по ссылке.
Что происходит в этомперегрузка?
Мы хотим избежать применения operator&
к типу, так как он может быть перегружен.
Стандарт гарантирует, что reinterpret_cast
может использоваться для этой работы (см. ответ @Matteo Italia: 5.2.10 / 10).
Boost добавляет некоторые тонкости с квалификаторами const
и volatile
, чтобы избежать предупреждений компилятора (и правильно используйте const_cast
для их удаления).
- Cast
T&
tochar const volatile&
- Обрезать
const
и volatile
- Применить оператор
&
, чтобы получить адрес - Привести обратно к
T*
Жонглирование const
/ volatile
- это немного чёрной магии, но оно упрощает работу (вместо обеспечения 4 перегрузок).Обратите внимание, что, поскольку T
является безусловным, если мы передадим ghost const&
, то T*
будет ghost const*
, таким образом, квалификаторы на самом деле не потеряны.
РЕДАКТИРОВАТЬ: Перегрузка указателя используется для указателя на функции, я несколько исправил приведенное выше объяснение.Я до сих пор не понимаю, почему это необходимо .
Следующий вывод идеона несколько суммирует это.