Home > Blockchain >  Passing pointer reference from template function to non-template function
Passing pointer reference from template function to non-template function

Time:07-10

I am attempting to move around a pointer by reference (T*&) between some template functions. Under certain conditions this pointer reference may get passed to a different function that accepts a void pointer reference (void*&). When I attempt to pass the templated type into the function accepting a void*&, it gives me the error:

error: cannot bind non-const lvalue reference of type 'void*&' to an rvalue of type 'void*'

This error is pretty self explanatory on its own. However I can't readily make sense of the error in context of the code. Here is a minimal reproduction of my error I was able to make in 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;
}

The error specifically complains about the invocation of NonTempFunct(void*&). As far as I am aware, there are no rvalues in this chain. They all have names and refer back to an automatically allocated variable.

I didn't stop here though, and fiddled with the code a bit. Using std::forward (NonTempFunct( std::forward<T&>(Param) );) or std::move (NonTempFunct( std::move(Param) );) when invoking NonTempFunct didn't change the error produced.

VERY curiously, when I switched the references in both TempFunct declarations to a universal reference (&&) the program did compile, however the wrong version was selected with SFINAE, suggesting the std::is_pointer_v<T> check failed with universal references.

The one thing that did work was a reinterpret_cast in the call to NonTempFunct (without universal references).

NonTempFunct( reinterpret_cast<void*&>(Param) );

That compiles. I fear I don't understand C well enough to make sense of these results. My specific questions are:

  1. Where is the rvalue from the initial error coming from?
  2. Why does the use of a universal reference cause std::is_pointer_v to fail?
  3. Why does a reinterpret_cast bypass these issues?

CodePudding user response:

Case 1

Here we discuss the reason for the mentioned error.

The problem is that param is an lvalue of type int* and it can be converted to a prvalue of type void* when passing it as the call argument in NonTempFunct( Param ); but the parameter of NonTempFunct is a non-const lvalue reference which cannot be bound to an rvalue.

Essentially, the result of the conversion(int*->void*) will be a prvalue and a non-const lvalue reference cannot be bound to that rvalue.

To solve this you can either make the parameter of NonTempFunct to be a const lvalue reference or simply a void* as shown below

Method 1

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

Working demo

Method 2

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

Working demo


Case 2

Here we discuss the reason when we use universal reference, the program compiles without any error.

When you make the function template's parameter to be T&& and use the call TempFunct( pValue ) then T is deduced to be int*& i.e., non const lvalue reference to a non const pointer to int.

This means that std::is_pointer_v<T> will be the same as std::is_pointer_v<int*&> which will be false. Demo.

This in turn means that the first overloaded version will be SFINAE'd OUT. And since the second version is viable(as it uses !std::is_pointer_v<T> which is the same as !std::is_pointer_v<int*&> and so is true ), it will be used and we will get the output Non pointer found. No op.

  • Related