Je tente de déplacer un pointeur par référence (T*&) entre certaines fonctions de modèle. Dans certaines conditions, ce pointeur par référence peut être transmis à une autre fonction qui accepte un pointeur par référence void (void*&). Lorsque j'essaie de passer le type template dans la fonction acceptant un void*&, cela me donne l'erreur :
error: cannot bind non-const lvalue reference of type 'void*&' to an rvalue of type 'void*'
Cette erreur est assez explicite en soi. Cependant, je n'arrive pas à comprendre l'erreur dans le contexte du code. Voici une reproduction minimale de mon erreur que j'ai pu faire dans Godbolt (x86_64 gcc 10.2) :
#include <iostream>
#include <type_traits>
void NonTempFunct(void*& Ptr)
{
std::cout << "Pointer Value: " << Ptr << ".\n";
}
template<typename T, typename = std::enable_if_t< std::is_pointer_v<T> >>
void TempFunct(T& Param)
{
std::cout << "Pointer found.\n";
NonTempFunct( Param );
}
template<typename T, typename = std::enable_if_t< !std::is_pointer_v<T> >, typename = void>
void TempFunct(T& Param)
{
std::cout << "Non pointer found. No op.\n";
}
int main()
{
int Value = 50;
int* pValue = &Value;
TempFunct( pValue );
return 0;
}
L'erreur concerne spécifiquement l'invocation de NonTempFunct(void*&). Pour autant que je sache, il n'y a pas de rvalues dans cette chaîne. Elles ont toutes un nom et renvoient à une variable allouée automatiquement.
Mais je ne me suis pas arrêté là, et j'ai bricolé un peu le code. En utilisant std::forward ( NonTempFunct( std::forward<T&>(Param) );
) ou std::move ( NonTempFunct( std::move(Param) );
) lors de l'invocation de NonTempFunct n'a pas changé l'erreur produite.
TRÈS curieusement, lorsque j'ai remplacé les références dans les deux déclarations de TempFunct par une référence universelle (&&), le programme a compilé, mais la mauvaise version a été sélectionnée avec SFINAE, ce qui suggère que l'option std::is_pointer_v<T>
Le contrôle a échoué avec les références universelles.
La seule chose qui a fonctionné est un reinterpret_cast dans l'appel à NonTempFunct (sans références universelles).
NonTempFunct( reinterpret_cast<void*&>(Param) );
Ça se compile. Je crains de ne pas comprendre suffisamment bien le C++ pour donner un sens à ces résultats. Mes questions spécifiques sont les suivantes :
- D'où vient la valeur r de l'erreur initiale ?
- Pourquoi l'utilisation d'une référence universelle fait-elle échouer std::is_pointer_v ?
- Pourquoi un reinterpret_cast contourne-t-il ces problèmes ?