2 votes

Passage d'un pointeur de référence d'une fonction modèle à une fonction non modèle

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 :

  1. D'où vient la valeur r de l'erreur initiale ?
  2. Pourquoi l'utilisation d'une référence universelle fait-elle échouer std::is_pointer_v ?
  3. Pourquoi un reinterpret_cast contourne-t-il ces problèmes ?

3voto

Anoop Rana Points 1

Cas 1

Nous examinons ici la raison de l'erreur mentionnée.

En problème c'est que param est une lvalue de type int* et il peut être converti en un prvalue de type void* en le passant comme argument d'appel dans NonTempFunct( Param ); mais le paramètre de NonTempFunct es un référence à une valeur l non-const qui ne peut pas être lié à une rvalue.

Essentiellement, le résultat de la conversion( int* -> void* ) sera une prvalue et une référence non-const lvalue ne pourra pas être liée à cette rvalue.

A résoudre Pour cela, vous pouvez soit faire en sorte que le paramètre de NonTempFunct pour être un const lvalue référence ou simplement un void* comme indiqué ci-dessous

Méthode 1

//----------------------vvvvv---------->added this
void NonTempFunct(void *const& Ptr)
{
    std::cout << "Pointer Value: " << Ptr << ".\n";
}

Démonstration de travail

Méthode 2

//----------------vvvvv---------->removed the reference
void NonTempFunct(void* Ptr)
{
    std::cout << "Pointer Value: " << Ptr << ".\n";
}

Démonstration de travail


Cas 2

Nous discutons ici de la raison pour laquelle lorsque nous utilisons la référence universelle, le programme se compile sans aucune erreur.

Lorsque vous faites en sorte que le paramètre du modèle de fonction soit T&& et utiliser l'appel TempFunct( pValue ) puis T est déduit de int*& c'est-à-dire, référence lvalue non constante à un pointeur non constant sur int .

Cela signifie que std::is_pointer_v<T> sera le même que std::is_pointer_v<int*&> qui sera fausse. Démo .

Cela signifie donc que la première version surchargée sera SFINAE'd OUT . Et comme la deuxième version est viable (puisqu'elle utilise !std::is_pointer_v<T> qui est identique à !std::is_pointer_v<int*&> et ainsi est vrai ), il sera utilisé et nous obtiendrons la sortie Non pointer found. No op.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X