Обновление: в 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*, таким образом, квалификаторы действительно не были потеряны.
РЕДАКТИРОВАТЬ: перегрузка указателя используется для указателя на функции, я несколько исправил выше объяснение. Я до сих пор не понимаю, почему это необходимо, хотя.
Следующий вывод идеона несколько суммирует это.