Mise à jour: en C ++ 11, on peut utiliser à la std::addressof
place de boost::addressof
.
Copions d'abord le code de Boost, moins le travail du compilateur autour des bits:
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 );
}
Que se passe-t-il si nous transmettons une référence à la fonction ?
Remarque: addressof
ne peut pas être utilisé avec un pointeur sur la fonction
En C ++, si void func();
est déclaré, alors func
est une référence à une fonction ne prenant aucun argument et ne renvoyant aucun résultat. Cette référence à une fonction peut être convertie de manière triviale en un pointeur vers une fonction - à partir de @Konstantin
: Selon 13.3.3.2 les deux T &
et T *
sont indiscernables pour les fonctions. La première est une conversion d'identité et la deuxième est une conversion de fonction en pointeur, toutes deux ayant le rang "Exact Match" (13.3.3.1.1 tableau 9).
La référence au passage de fonctionaddr_impl_ref
, il y a une ambiguïté dans la résolution de surcharge pour le choix de f
, qui est résolue grâce à l'argument factice 0
, qui est une int
première et pourrait être promue en long
(Conversion intégrale).
Ainsi, nous renvoyons simplement le pointeur.
Que se passe-t-il si nous passons un type avec un opérateur de conversion?
Si l'opérateur de conversion donne un T*
alors nous avons une ambiguïté: pour f(T&,long)
une promotion intégrale est requise pour le deuxième argument tandis que pour f(T*,int)
l'opérateur de conversion est appelé sur le premier (grâce à @litb)
C'est à ce moment-là que addr_impl_ref
commence. La norme C ++ stipule qu'une séquence de conversion peut contenir au plus une conversion définie par l'utilisateur. En encapsulant le type addr_impl_ref
et en forçant déjà l'utilisation d'une séquence de conversion, nous "désactivons" tout opérateur de conversion fourni avec le type.
Ainsi, la f(T&,long)
surcharge est sélectionnée (et la promotion intégrale effectuée).
Que se passe-t-il pour tout autre type?
Ainsi la f(T&,long)
surcharge est sélectionnée, car là le type ne correspond pas au T*
paramètre.
Remarque: d'après les remarques dans le fichier concernant la compatibilité Borland, les tableaux ne se désintègrent pas en pointeurs, mais sont passés par référence.
Que se passe-t-il dans cette surcharge?
Nous voulons éviter d'appliquer operator&
au type, car il peut avoir été surchargé.
Les garanties standard qui reinterpret_cast
peuvent être utilisées pour ce travail (voir la réponse de @Matteo Italia: 5.2.10 / 10).
Boost ajoute quelques subtilités avec les qualificatifs const
et volatile
pour éviter les avertissements du compilateur (et utilisez correctement a const_cast
pour les supprimer).
- Diffuser
T&
surchar const volatile&
- Dénuder le
const
etvolatile
- Appliquer l'
&
opérateur pour prendre l'adresse
- Rejeter sur un
T*
Le const
/ la volatile
jonglerie est un peu de la magie noire, mais elle simplifie le travail (plutôt que de fournir 4 surcharges). Notez que puisque T
n'est pas qualifié, si nous passons a ghost const&
, alors T*
est ghost const*
, donc les qualificatifs n'ont pas vraiment été perdus.
EDIT: la surcharge du pointeur est utilisée pour le pointeur vers les fonctions, j'ai quelque peu modifié l'explication ci-dessus. Je ne comprends toujours pas pourquoi c'est nécessaire .
La sortie d'ideone suivante résume cela, un peu.