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