Jonathan Mee
Jonathan Mee

Reputation: 38919

Why do I Have to reinterpret_cast Pointer Pointers?

So this static_cast code is totally legal:

int n = 13;
void* pn = static_cast<void*>(&n);
void** ppn = &pn;

Yet this has to be made into a reinterpret_cast to compile:

int n = 13;
int* foo = &n;
void** bar = static_cast<void**>(&foo);

If I don't change it I get the error:

error C2440: static_cast: cannot convert from int ** to void ** note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

So I take it the issue is "the types are unrelated". Yet I still don't understand, if it's OK when going from an int* to void* how can they be unrelated as a int** and a void**?

Upvotes: 4

Views: 1566

Answers (2)

curiousguy
curiousguy

Reputation: 8270

void* pn = static_cast<void*>(&n);

is an implicit conversion; you can also write

void *pn = &n;

It means that pn stores a pointer to some object type; the programmer is responsible for knowing what that object type is. To cast back you need a static_cast:

int *pi = static_cast<int*>(pn);

Note that using static_cast to cast to any type significantly different than the original (float is significantly different, const int isn't) is a way of doing a reinterpretation. You should spell that reinterpret_cast not implicit conversion (or static_cast) followed by static_cast.

This is the whole purpose of void*, an concept tracing back to C, where casts are spelled with C-style casts obviously (not static_cast...) but have otherwise identical meanings.

Going back to the syntax of declarations in C and C++:

The declaration of a pointer to int is int (*pi); (parentheses are useless but help illustrate the point), you can read it like: I declare that expression (*pi) has type int. You can read a function declaration that way: in int f(int i); I declare that if i has type int then f(i) has type int.

The declaration void (*pi); looks like a pointer to a void but there is no such thing as an object of type void, the expression *pi isn't even well formed, it doesn't make sense. It's a special case in the type system: the syntax says "pointer to void", semantic says "pointer to something".

In C and C++, a pointer object is a first class object and you can take its address and have a pointer to a pointer, etc. (Contrast with Java where references like other fundamental types aren't class objects.)

So you can have a int**, int***... pointers to (pointers ... to int); you can have for the same reason void**: declared like a pointer to (pointer to void), semantically a pointer to (pointer to something).

Just like a pointer to int can't be assigned to a pointer to float without a cast:

float *f = &i; // ill-formed

because of the type mismatch, a type different from void** can't be assigned to a void**: the result of dereferencing a void** must be a void* object.

Upvotes: 0

eerorika
eerorika

Reputation: 238311

int is in no way related to void. The same goes for int** and void** and so they cannot be converted using static_cast.

void* however, is special. Any data pointer type (including int*) can be static_cast into void* and back, despite no type being related to void (even further, the conversion to void* doesn't need a cast, as it is implicit). int* doesn't have this property, nor does void** and nor does any other pointer besides void*.


The additional freedoms that have been granted to void* come with additional restrictions. void* cannot be indirected, nor can it be used with pointer arithmetic. These restrictions are only possible because there can never be an object of type void. Or from opposite point of view, objects of void cannot exist because of these restrictions.

void** cannot be given those freedoms, because it cannot be given the same restrictions. It cannot be given those restrictions because void* objects do exist and they need to exist. If we couldn't indirect or iterate void**, then we couldn't use arrays of void* for example.

Upvotes: 8

Related Questions