2010-07-02 7 views
4

J'ai une fonction address_of, qui renvoie un Pointer (encapsulant un shared_ptr) à son argument. address_of doit fonctionner à la fois sur lvalues ​​et rvalues, donc il existe deux versions de address_of: l'une acceptant une référence et l'autre acceptant une référence rvalue. Depuis prendre l'adresse d'un temporaire est une mauvaise chose ™, la version rvalue de address_of doit effectuer une construction de mouvement afin que le Pointer possède réellement quelque chose. La mise en œuvre est simple:Prendre l'adresse d'un temporaire, avec un Twist

template<class T> 
inline Pointer address_of(T& value) { 
    return Pointer(&value); 
} 

template<class T> 
inline Pointer address_of(T&& value) { 
    return Pointer(new T(std::move(value))); 
} 

Et prenant l'adresse d'un ouvrage temporaire comme prévu:

Pointer p = address_of(Derived()); 

Mais quand je test avec le code suivant:

Base* object = new Derived(); 
Pointer p = address_of(*object); 

GCC se plaint que la appeler au address_of est ambigu:

error: call of overloaded ‘address_of(Base&)’ is ambiguous 
note: candidates are: Pointer address_of(T&) [with T = Base] 
note:     Pointer address_of(T&&) [with T = Base&] 

J'étais sous l'impression que * unaire renvoie toujours une lvalue, auquel cas la version de rvalue ne devrait même pas être considérée. Qu'est-ce qui se passe exactement ici?

Répondre

2

Le problème est causé par la désintégration de référence: (terme correct est "l'effondrement de référence")

template < typename T > 
void f(T && t) { .... } 

int x; f(x); // what is f()? 

La réponse à la question dans le code est que f() est:

void f(T& && t) { .... } 

Qui, en raison de la décroissance de référence se transforme en ceci:

void f(T& t) { .... } 

Comme vous pouvez vous attendre, ce sera bien sûr incertain s avec quoi que ce soit défini comme:

template < typename T > 
void f(T & t) { .... } 

Cela pourrait fonctionner (version fixe):

#include <type_traits> 
#include <utility> 

template < typename T > 
struct pointer 
{ 
    pointer(T& t) {} 
    pointer(T&& t) {} 
}; 

template < typename T > 
pointer<typename std::remove_reference<T>::type> 
address_of(T && t) 
{ 
    return pointer<typename std::remove_reference<T>::type>(std::forward<T>(t)); 
} 

int main() 
{ 
    int x = 5; 
    pointer<int> o = address_of(x); 
    pointer<int> p = address_of(5); 
} 

La raison en est que ce genre de choses de désintégration de référence ne se produit que dans les fonctions qui sont basés sur un modèle sur T. Dans ce cas, votre La classe pointeur est, mais les constructeurs ne sont pas eux-mêmes des modèles et donc T & n'est jamais un T valide pour la version T & &.

La première version présentait toujours le même problème que votre code puisque address_of utilisait simplement T comme paramètre de modèle pour le pointeur. Nous avons réellement besoin du type brut.

+0

Cela a du sens, merci. Des pensées sur la façon d'atteindre le comportement que je * veux *? –

+0

Qu'est-ce que la décroissance de référence? –

+0

@Jon - un. Pas positif ça va marcher mais je l'ai mis dans ma réponse. –

Questions connexes