Aktualizacja: w C ++ 11 std::addressof
zamiast boost::addressof
.
Najpierw skopiujmy kod z Boost, bez kompilatora obejść bity:
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 );
}
Co się stanie, jeśli przekażemy odniesienie do funkcji ?
Uwaga: addressof
nie można używać ze wskaźnikiem do funkcji
W C ++ if void func();
jest zadeklarowane, to func
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ówno T &
i T *
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 wyboru f
, który jest rozwiązany dzięki argumentu zastępczą 0
, która jest int
pierwszym i może być podniesione na long
(całka konwersji).
W ten sposób po prostu zwracamy wskaźnik.
Co się stanie, jeśli przekażemy typ z operatorem konwersji?
Jeśli operator konwersji daje a, T*
to mamy niejednoznaczność: ponieważ f(T&,long)
Integral Promotion jest wymagana dla drugiego argumentu, a dla f(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 typ addr_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).
Co dzieje się z każdym innym typem?
W ten sposób f(T&,long)
wybrane jest przeciążenie, ponieważ tam typ nie pasuje do T*
parametru.
Uwaga: z uwag w pliku dotyczących kompatybilności Borlanda, tablice nie rozpadają się na wskaźniki, ale są przekazywane przez odniesienie.
Co się dzieje w tym przeciążeniu?
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ąć).
- Przesyłaj
T&
dochar const volatile&
- Usuń
const
ivolatile
- Zastosuj
&
operatora, aby przejąć adres
- Odrzuć z powrotem do
T*
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 a ghost const&
, to T*
jest ghost 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ą.