Aktualizacja: w C ++ 11 std::addressofzamiast 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: addressofnie można używać ze wskaźnikiem do funkcji
W C ++ if void func();jest zadeklarowane, to funcjest 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_refnie jest niejednoznaczność rozdzielczości przeciążeniem wyboru f, który jest rozwiązany dzięki argumentu zastępczą 0, która jest intpierwszym 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_refzaczyna się. Standard C ++ nakazuje, aby sekwencja konwersji zawierała co najwyżej jedną konwersję zdefiniowaną przez użytkownika. Zawijając typ addr_impl_refi 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_castmożna wykorzystać w tej pracy (patrz odpowiedź @Matteo Italia: 5.2.10 / 10).
Boost dodaje kilka dodatków z kwalifikatorami consti, volatileaby uniknąć ostrzeżeń kompilatora (i poprawnie użyj a, const_castaby je usunąć).
- Przesyłaj
T&dochar const volatile&
- Usuń
constivolatile
- 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ż Tjest 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ą.