Оновлення: на 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).
Посилання на функцію проходить через addr_impl_ref, існує невизначеність у вирішенні перевантаження для вибору f, яка вирішується завдяки фіктивному аргументу 0, який є intпершим і може бути підвищений до long(інтегрального перетворення).
Таким чином, ми просто повертаємо вказівник.
Що станеться, якщо ми передамо тип оператору перетворення?
Якщо оператор конверсії дає a, T*ми маємо неоднозначність: для f(T&,long)інтегрального просування потрібно другий аргумент, тоді як для 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класифікатори, щоб уникнути попереджень компілятора (і належним чином використовувати const_castдля їх видалення).
- Cast
T&доchar const volatile&
- Смугайте
constіvolatile
- Застосуйте
&оператора, щоб взяти адресу
- Повернення до а
T*
const/ volatileЖонглювання трохи чорної магія, але це спростить роботу (а не надання 4 перевантажень). Зауважимо, що оскільки Tце некваліфіковане, якщо ми проходимо а ghost const&, то T*є ghost const*, таким чином кваліфікатори насправді не програли.
EDIT: перевантаження вказівника використовується для вказівника на функції, я дещо змінив вищевказане пояснення. Я досі не розумію, навіщо це потрібно .
Наступний вихід ідеону дещо підсумовує це.