Rozważ następujący program:
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 :'(
}
Jak uzyskać clyde
adres?
Szukam rozwiązania, które sprawdzi się równie dobrze na wszystkich typach obiektów. Rozwiązanie C ++ 03 byłoby fajne, ale interesują mnie też rozwiązania C ++ 11. Jeśli to możliwe, unikajmy wszelkich zachowań specyficznych dla implementacji.
Znam std::addressof
szablon funkcji C ++ 11 , ale nie jestem zainteresowany użyciem go tutaj: chciałbym zrozumieć, w jaki sposób osoba wdrażająca bibliotekę standardową może zaimplementować ten szablon funkcji.
c++
c++11
operator-overloading
memory-address
James McNellis
źródło
źródło
:)
)CComPtr<>
iCComQIPtr<>
przeładowanyoperator&
Odpowiedzi:
Aktualizacja: w C ++ 11
std::addressof
zamiastboost::addressof
.Najpierw skopiujmy kod z Boost, bez kompilatora obejść bity:
Uwaga:
addressof
nie można używać ze wskaźnikiem do funkcjiW C ++ if
void func();
jest zadeklarowane, tofunc
jest odwołaniem do funkcji nie pobierającej argumentu i nie zwracającej wyniku. To odniesienie do funkcji można w trywialny sposób przekształcić we wskaźnik do funkcji - z@Konstantin
: Zgodnie z 13.3.3.2 zarównoT &
iT *
są nierozróżnialne dla funkcji. Pierwsza z nich to konwersja tożsamości, a druga to konwersja funkcji do wskaźnika, obie mają rangę „Dokładne dopasowanie” (13.3.3.1.1 tabela 9).Odniesienie do funkcji przejść
addr_impl_ref
nie jest niejednoznaczność rozdzielczości przeciążeniem wyboruf
, który jest rozwiązany dzięki argumentu zastępczą0
, która jestint
pierwszym i może być podniesione nalong
(całka konwersji).W ten sposób po prostu zwracamy wskaźnik.
Jeśli operator konwersji daje a,
T*
to mamy niejednoznaczność: ponieważf(T&,long)
Integral Promotion jest wymagana dla drugiego argumentu, a dlaf(T*,int)
konwersji operator jest wywoływany na pierwszym (dzięki @litb)Wtedy
addr_impl_ref
zaczyna się. Standard C ++ nakazuje, aby sekwencja konwersji zawierała co najwyżej jedną konwersję zdefiniowaną przez użytkownika. Zawijając typaddr_impl_ref
i wymuszając już użycie sekwencji konwersji, „wyłączamy” każdy operator konwersji, z którym ten typ pochodzi.W ten sposób
f(T&,long)
wybierane jest przeciążenie (i wykonywana jest promocja integralna).W ten sposób
f(T&,long)
wybrane jest przeciążenie, ponieważ tam typ nie pasuje doT*
parametru.Uwaga: z uwag w pliku dotyczących kompatybilności Borlanda, tablice nie rozpadają się na wskaźniki, ale są przekazywane przez odniesienie.
Chcemy uniknąć stosowania
operator&
do typu, ponieważ mógł być przeciążony.Standardowe gwarancje, które
reinterpret_cast
można wykorzystać w tej pracy (patrz odpowiedź @Matteo Italia: 5.2.10 / 10).Boost dodaje kilka dodatków z kwalifikatorami
const
i,volatile
aby uniknąć ostrzeżeń kompilatora (i poprawnie użyj a,const_cast
aby je usunąć).T&
dochar const volatile&
const
ivolatile
&
operatora, aby przejąć adresT*
const
/volatile
Żonglerka jest nieco czarnej magii, ale ma uprościć pracę (raczej niż dostarczanie 4 przeciążeń). Zauważ, że ponieważT
jest niekwalifikowany, jeśli zdamy aghost const&
, toT*
jestghost const*
, więc kwalifikatory tak naprawdę nie zostały utracone.EDYCJA: przeciążenie wskaźnika służy do wskazywania wskaźników do funkcji, nieco poprawiłem powyższe wyjaśnienie. Nadal nie rozumiem, dlaczego jest to konieczne .
Poniższe wyniki ideone nieco to podsumowują.
źródło
f
przeciążenia są szablonami funkcji, podczas gdy są one zwykłymi funkcjami składowymi klasy szablonu, dziękuję za wskazanie. (Teraz muszę tylko dowiedzieć się, do czego służy przeciążenie, jakaś wskazówka?)char*
”. Dziękuję Matthieu.T*
? EDYCJA: Teraz widzę. Mógłby, ale z0
argumentem skończyłby się na krzyż , więc byłoby niejednoznaczne.Użyj
std::addressof
.Możesz myśleć o tym jako o wykonywaniu następujących czynności za kulisami:
Istniejące implementacje (w tym Boost.Addressof) zrobić dokładnie to, po prostu biorąc dodatkową opiekę
const
ivolatile
kwalifikacji.źródło
Sztuczka stojąca za
boost::addressof
i implementacja dostarczona przez @Luc Danton opiera się na magiireinterpret_cast
; standard wyraźnie stwierdza w §5.2.10, ak.10Teraz pozwala nam to przekonwertować dowolne odwołanie do obiektu na a
char &
(z kwalifikacją cv, jeśli referencja jest kwalifikowana jako cv), ponieważ dowolny wskaźnik można przekonwertować na (prawdopodobnie kwalifikowany cv)char *
. Teraz, gdy mamy achar &
, przeciążenie operatora na obiekcie nie jest już istotne i możemy uzyskać adres za pomocą&
operatora wbudowanego .Implementacja przyspieszenia dodaje kilka kroków do pracy z obiektami kwalifikowanymi w cv: pierwszy
reinterpret_cast
jest wykonywanyconst volatile char &
, w przeciwnym razie zwykłechar &
rzutowanie nie działałoby dlaconst
i / lubvolatile
referencji (reinterpret_cast
nie można ich usunąćconst
). Następnie usuwane jestconst
i zavolatile
pomocąconst_cast
, adres jest brany za pomocą&
, a końcowereinterpet_cast
do „prawidłowego” typu jest wykonywane.const_cast
Jest potrzebne do usunięciaconst
/volatile
, które mogły zostać dodane do const / Referencje lotnych, ale nie „szkodzić”, co to jestconst
/volatile
odniesienia w pierwszej kolejności, ponieważ ostatecznareinterpret_cast
ponownie dodać CV kwalifikacje, czy to tam na pierwszym miejscu (reinterpret_cast
nie można usunąć,const
ale można go dodać).Co do reszty kodu w programieaddressof.hpp
, wydaje się, że większość z niego dotyczy obejść.static inline T * f( T * v, int )
Wydaje się być potrzebna tylko dla kompilatora Borland, ale jego obecność wprowadza koniecznośćaddr_impl_ref
, inaczej typy wskaźnik byłby złapany przez tego drugiego przeciążenia.Edycja : różne przeciążenia mają inną funkcję, patrz @Matthieu M. doskonała odpowiedź .Cóż, tego też nie jestem już pewien; Powinienem dokładniej zbadać ten kod, ale teraz gotuję obiad :), przyjrzę się temu później.
źródło
void func();
boost::addressof(func);
. Jednak usunięcie przeciążenia nie zapobiega kompilowaniu kodu przez gcc 4.3.4 i generowaniu tych samych danych wyjściowych, więc nadal nie rozumiem, dlaczego konieczne jest posiadanie tego przeciążenia.Widziałem implementację
addressof
zrób to:Nie pytajcie mnie, jak to jest zgodne!
źródło
char*
jest wymienionym wyjątkiem od reguł aliasingu.reinterpret_cast<char*>
dobrze zdefiniowane.[unsigned] char *
a tym samym odczytywać reprezentację obiektu wskazywanego obiektu. To kolejny obszar, którychar
ma specjalne przywileje.Przyjrzyj się boost :: addressof i jego implementacji.
źródło
addressof
zwraca sam wskaźnik. Można się spierać, czy tego chciał użytkownik, czy nie, ale tak to określił.addr_impl_ref
, więc przeciążenie wskaźnika nigdy nie powinno być nazywane ...