Оновлення: на 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: перевантаження вказівника використовується для вказівника на функції, я дещо змінив вищевказане пояснення. Я досі не розумію, навіщо це потрібно .
Наступний вихід ідеону дещо підсумовує це.