Considérez le programme suivant:
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 :'(
}
Comment obtenir l clyde
'adresse de?
Je recherche une solution qui fonctionnera aussi bien pour tous les types d'objets. Une solution C ++ 03 serait bien, mais je suis également intéressé par les solutions C ++ 11. Si possible, évitons tout comportement spécifique à l'implémentation.
Je connais le std::addressof
modèle de fonction de C ++ 11 , mais je ne suis pas intéressé à l'utiliser ici: j'aimerais comprendre comment un implémenteur de bibliothèque standard pourrait implémenter ce modèle de fonction.
c++
c++11
operator-overloading
memory-address
James McNellis
la source
la source
:)
)CComPtr<>
etCComQIPtr<>
avoir une surchargeoperator&
Réponses:
Mise à jour: en C ++ 11, on peut utiliser à la
std::addressof
place deboost::addressof
.Copions d'abord le code de Boost, moins le travail du compilateur autour des bits:
Remarque:
addressof
ne peut pas être utilisé avec un pointeur sur la fonctionEn C ++, si
void func();
est déclaré, alorsfunc
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 deuxT &
etT *
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 fonction
addr_impl_ref
, il y a une ambiguïté dans la résolution de surcharge pour le choix def
, qui est résolue grâce à l'argument factice0
, qui est uneint
première et pourrait être promue enlong
(Conversion intégrale).Ainsi, nous renvoyons simplement le pointeur.
Si l'opérateur de conversion donne un
T*
alors nous avons une ambiguïté: pourf(T&,long)
une promotion intégrale est requise pour le deuxième argument tandis que pourf(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 typeaddr_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).Ainsi la
f(T&,long)
surcharge est sélectionnée, car là le type ne correspond pas auT*
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.
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
etvolatile
pour éviter les avertissements du compilateur (et utilisez correctement aconst_cast
pour les supprimer).T&
surchar const volatile&
const
etvolatile
&
opérateur pour prendre l'adresseT*
Le
const
/ lavolatile
jonglerie est un peu de la magie noire, mais elle simplifie le travail (plutôt que de fournir 4 surcharges). Notez que puisqueT
n'est pas qualifié, si nous passons aghost const&
, alorsT*
estghost 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.
la source
f
surcharges étaient des modèles de fonctions, alors que ce sont des fonctions membres régulières d'une classe de modèle, merci de le signaler. (Maintenant, j'ai juste besoin de comprendre à quoi sert la surcharge, un conseil?)char*
». Merci Matthieu.T*
? EDIT: Maintenant, je vois. Ce serait le cas, mais avec l'0
argumentation, cela aboutirait à un entrecroisement , ce serait donc ambigu.Utilisez
std::addressof
.Vous pouvez y penser comme faisant ce qui suit dans les coulisses:
Les implémentations existantes (y compris Boost.Addressof) font exactement cela, en prenant simplement soin
const
et envolatile
qualification.la source
L'astuce derrière
boost::addressof
et l'implémentation fournie par @Luc Danton repose sur la magie dureinterpret_cast
; la norme indique explicitement au §5.2.10 ¶10 queMaintenant, cela nous permet de convertir une référence d'objet arbitraire en a
char &
(avec une qualification cv si la référence est qualifiée cv), car tout pointeur peut être converti en a (éventuellement qualifié cv)char *
. Maintenant que nous avons unchar &
, la surcharge de l'opérateur sur l'objet n'est plus pertinente, et nous pouvons obtenir l'adresse avec l'&
opérateur intégré .L'implémentation boost ajoute quelques étapes pour travailler avec des objets qualifiés cv: la première
reinterpret_cast
est faite pourconst volatile char &
, sinon unchar &
cast brut ne fonctionnerait pas pourconst
et / ouvolatile
références (reinterpret_cast
ne peut pas supprimerconst
). Ensuite,const
etvolatile
est supprimé avecconst_cast
, l'adresse est prise avec&
, et un finalreinterpet_cast
au type "correct" est effectué.Le
const_cast
est nécessaire pour supprimer leconst
/volatile
qui aurait pu être ajouté à des références non const / volatiles, mais il ne "nuit" pas à ce qui était une référenceconst
/volatile
en premier lieu, car le finalreinterpret_cast
ajoutera à nouveau la qualification cv si c'était là en premier lieu (reinterpret_cast
ne peut pas supprimer leconst
mais peut l'ajouter).En ce qui concerne le reste du codeaddressof.hpp
, il semble que la majeure partie soit pour des solutions de contournement. Lestatic inline T * f( T * v, int )
semble être nécessaire uniquement pour le compilateur Borland, mais sa présence en introduit le besoinaddr_impl_ref
, sinon les types de pointeurs seraient interceptés par cette seconde surcharge.Edit : les différentes surcharges ont une fonction différente, voir @Matthieu M. excellente réponse .Eh bien, je n'en suis plus sûr non plus; Je devrais approfondir ce code, mais maintenant je prépare le dîner :), je vais y jeter un œil plus tard.
la source
void func();
boost::addressof(func);
. Cependant, la suppression de la surcharge n'empêche pas gcc 4.3.4 de compiler le code et de produire la même sortie, donc je ne comprends toujours pas pourquoi il est nécessaire d'avoir cette surcharge.J'ai vu une implémentation de
addressof
faire ceci:Ne me demandez pas à quel point c'est conforme!
la source
char*
est l'exception répertoriée pour les règles d'alias de type.reinterpret_cast<char*>
bien définie.[unsigned] char *
et ainsi de lire la représentation d'objet de l'objet pointé. Ceci est un autre domaine oùchar
a des privilèges spéciaux.Jetez un œil à boost :: addressof et à son implémentation.
la source
addressof
renvoie le pointeur lui-même. On peut se demander si c'est ce que l'utilisateur voulait ou non, mais c'est ainsi qu'il l'a spécifié.addr_impl_ref
, donc la surcharge du pointeur ne devrait jamais être appelée ...