Рассмотрим следующую программу:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Как я могу получить clyde
адрес?
Я ищу решение, которое будет одинаково хорошо работать для всех типов объектов. Решение на C ++ 03 было бы неплохо, но я также заинтересован в решениях на C ++ 11. Если возможно, давайте избегать поведения, специфичного для реализации.
Мне известен std::addressof
шаблон функции C ++ 11 , но я не заинтересован в его использовании здесь: я хотел бы понять, как разработчик стандартной библиотеки может реализовать этот шаблон функции.
c++
c++11
operator-overloading
memory-address
Джеймс МакНеллис
источник
источник
:)
)CComPtr<>
иCComQIPtr<>
перегруженыoperator&
Ответы:
Обновление: в C ++ 11 можно использовать
std::addressof
вместоboost::addressof
.Давайте сначала скопируем код из Boost, за исключением работы компилятора с битами:
Примечание:
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
квалификаторами, чтобы избежать предупреждений компилятора (и правильно используйте aconst_cast
для их удаления).T&
кchar const volatile&
const
иvolatile
&
оператору, чтобы получить адресT*
const
/volatile
Жонглирование немного черной магия, но это упростит работу (а не предоставление 4 перегрузок). Обратите внимание, что, посколькуT
мы безоговорочно, если мы передаем aghost const&
, тоT*
естьghost const*
, таким образом, квалификаторы действительно не были потеряны.РЕДАКТИРОВАТЬ: перегрузка указателя используется для указателя на функции, я несколько исправил выше объяснение. Я до сих пор не понимаю, почему это необходимо, хотя.
Следующий вывод идеона несколько суммирует это.
источник
f
перегружают шаблоны функций, тогда как они являются обычными функциями-членами класса шаблонов, спасибо за указание на это. (Теперь мне просто нужно выяснить, в чем польза от перегрузки, какой-нибудь совет?)char*
». Спасибо, Матье.T*
? РЕДАКТИРОВАТЬ: Теперь я вижу. Это было бы, но с0
аргументом это закончилось бы в перекрещивании , поэтому было бы неоднозначно.Использование
std::addressof
.Вы можете думать об этом как о следующих действиях:
Существующие реализации ( в том числе Boost.Addressof) делать то , что, только принимая дополнительный уход
const
иvolatile
квалификацию.источник
Уловка позади
boost::addressof
и реализация, обеспеченная @Luc Danton, полагается на волшебствоreinterpret_cast
; Стандарт в §5.2.10 ¶10 прямо заявляет, чтоТеперь это позволяет нам преобразовать произвольную ссылку на объект в
char &
(с квалификацией cv, если ссылка квалифицирована как cv), потому что любой указатель может быть преобразован в (возможно, квалифицированную cv)char *
. Теперь, когда у нас естьchar &
, перегрузка оператора на объекте больше не актуальна, и мы можем получить адрес с помощью встроенного&
оператора.Реализация boost добавляет несколько шагов для работы с cv-квалифицированными объектами: первый
reinterpret_cast
выполняетсяconst volatile char &
, иначе обычноеchar &
приведение не будет работатьconst
и / илиvolatile
ссылки (reinterpret_cast
не могут быть удаленыconst
). Затемconst
иvolatile
удаляется сconst_cast
, адрес берется с&
, и окончательныйreinterpet_cast
к «правильному» типу делается.const_cast
Необходимо , чтобы удалитьconst
/ ,volatile
которые могли быть добавлены к неконстантному / летучим ссылкам, но это не «вреду» , что былоconst
/volatile
ссылкой на первом месте, потому что окончательныеreinterpret_cast
будет повторно добавить резюме-квалификацию , если это было там на первом месте (reinterpret_cast
не может удалить,const
но может добавить его).Что касается остального кода вaddressof.hpp
, кажется, что большая часть этого для обходных путей.static inline T * f( T * v, int )
, Кажется, нужно только для компилятора Borland, но его присутствие вводит необходимостьaddr_impl_ref
, в противном случае типов указателей будут пойманы второй перегрузкой.Редактировать : различные перегрузки имеют разные функции, см. @Matthieu M. Отличный ответ .Ну, я больше не уверен в этом; Я должен дополнительно изучить этот код, но сейчас я готовлю ужин :), я посмотрю его позже.
источник
void func();
boost::addressof(func);
. Однако удаление перегрузки не мешает gcc 4.3.4 скомпилировать код и выдать тот же вывод, поэтому я до сих пор не понимаю, почему необходима эта перегрузка.Я видел реализацию
addressof
сделать это:Не спрашивайте меня, как это соответствует!
источник
char*
перечисленное исключение для правил наложения типов.reinterpret_cast<char*>
хорошо определен.[unsigned] char *
и, таким образом, читать объектное представление указанного объекта. Это еще одна область, гдеchar
есть особые привилегии.Посмотрите на boost :: addressof и его реализацию.
источник
addressof
возвращает сам указатель. Можно спорить, хотел ли пользователь того или нет, но это так, как было указано.addr_impl_ref
, поэтому перегрузка указателя никогда не должна вызываться ...