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:
- Where is the rvalue from the initial error coming from?
- Why does the use of a universal reference cause std::is_pointer_v to fail?
- Why does a reinterpret_cast bypass these issues?