Reputation: 118742
Why can't I pass a void*
by reference? The compiler allows me to declare a function with the following signature:
static inline void FreeAndNull(void*& item)
But when I try to call it, I get the following error:
Error 1 error C2664: 'FreeAndNull' : cannot convert parameter 1 from 'uint8_t *' to 'void *&'
Casting it to void*
doesn't work either
Also, are there any workarounds?
Upvotes: 7
Views: 13272
Reputation: 40849
Casting to void* doesn't work for a reason much simpler than what is being explained elsewhere: casting creates rvalues unless you explicitly specify a reference. You can't pass an rvalue off as a non-const reference.
Prove it? Try this:
void fun(void * &) {}
int main() {
int * x;
void * x2 = x;
fun(x);
}
If it's appropriate for you use, try this:
void fun(void * const&);
Now not only does casting work, its implicit.
Casting to reference can be dangerous. It does actually cause your code to compile but I won't speak to its behavior:
void fun(void *&) { }
int main() {
int * x;
fun((void*&)x);
}
My bet is that this will do very-bad-things(tm) since you're actually doing a reinterpret_cast here rather than a static cast.
Upvotes: 2
Reputation: 91270
If you take a void * by reference, you have to pass an actual void *, not an uint8_t *.
Try this instead:
template<typename T> inline void FreeAndNull(T * & V) { free(V); V = 0; }
EDIT: Modified sample to better reflect the OP's function name, and to address @6502's entirely correct comment.
Upvotes: 6
Reputation: 372694
The answer is yes, you can pass a void*
by reference, and the error you're getting is unrelated to that. The problem is that if you have a function that takes void*
by reference, then you can only pass in variables that actually are void*
s as a parameter. There's a good reason for this. For example, suppose you have this function:
void MyFunction(void*& ptr) {
ptr = malloc(137); // Get a block of raw memory
}
int* myIntArray;
MyFunction(myIntArray); // Error- this isn't legal!
The above code is illegal because of the indicated call, and for good reason. If we could pass in myIntArray
into MyFunction
, it would get reassigned to point to a buffer of type void*
that isn't an int
array. Consequently, on return from the function your int*
would be pointing at an array of type void*
, subverting the type system. This isn't to say that C++ has a strong type system - it doesn't - but if you're going to subvert it you have to explicitly put some casts in.
You similarly can't do this:
void MyFunction(void*& ptr) {
ptr = malloc(137); // Get a block of raw memory
}
int* myIntArray;
MyFunction((void*)myIntArray); // Error- this isn't legal!
As a good reference as to why you can't do this, think about this code:
void OtherFunction(int& myInt) {
myInt = 137;
}
double myDouble;
OtherFunction((int)myDouble); // Also error!
This isn't legal because if you tried passing in a double
into a function that took an int&
, then since int
and double
have fundamentally different binary representations, you'd end up clobbering the bits of the double
with meaningless data. If this cast were to be legal, it would be possible to do Bad Things like this.
So in short, yes, you can take in a void* &
, but if you do you have to pass in real void*
s. As Erik pointed out above, if you want to free and zero a pointer templates are the way to go.
If you really do want to pass in a uint_8*
into this your function, you could do so like this:
uint_8* uPtr = /* ... */
void* ptr = uPtr;
FreeAndNull(ptr);
uPtr = (uint_8*) ptr;
This requires explicit casting to tell the compiler "Yes, I know this may be unsafe, but I'm going to do it anyway."
Hope this helps!
Upvotes: 9