I'm currently working on a project where I often have to build linked lists of various C structs. Since I don't want to keep repeating myself setting next
pointers, I wrote some helper templates, but soon found out that it falls apart if one of the next
fields is a pointer-to-const.
My linked list elements look something like this:
struct WorkingElementType {
void *pNext;
/* stuff */
};
struct TroublesomeElementType {
const void *pNext;
/* stuff */
};
In reality, there are of course a lot more of these structs. My helper functions have to keep a pointer to the last element's pNext
field in order to write to it when the linked list gets extended, so I went for a void **ppNext = &last->pNext
. Unfortunately, that of course breaks down with TroublesomeElementType
and its const void *pNext
.
In the end, what I'd like to achieve is this:
void **m_ppNext;
/* In one function */
m_ppNext = &last->pNext;
/* In a different function, extending the list */
T *elementToAppend = ...;
*m_ppNext = elementToAppend;
I solved this by using a std::variant<void **, const void **> ppNext
instead, but using a std::variant
and std::visit
just for a difference in constness that doesn't even affect the code's function feels like a bit of a waste.
That's why I'm wondering: Is it legal to use const_cast
here to cast away const and stuff the const void **
into a void **
only for updating the pointed-to pointer? No const object actually gets modified, after all.
In other words: I'm not sure whether it's legal to alias const void*
and void *
. (My gut feeling says no, it's not legal because these are incompatible types, but I don't know for sure.)
The C++ standard in question is C++20.
Here's some simple example code:
#include <variant>
int g_i = 42;
/* This is legal */
void setIntPtr1(std::variant<int **, const int **> v) {
std::visit([](auto& p) { *p = &g_i; }, v);
}
int testNonConst1() {
int *i;
setIntPtr1(&i);
return *i;
}
int testConst1() {
const int *i;
setIntPtr1(&i);
return *i;
}
/* But I'm not sure about this */
void setIntPtr2(int **p) {
*p = &g_i;
}
int testNonConst2() {
int *i;
setIntPtr2(&i);
return *i;
}
int testConst2() {
const int *i;
setIntPtr2(const_cast<int **>(&i)); // Is this legal?
return *i;
}
On Godbolt, all of the various test...
functions compile to the exact same assembly, but I don't know if testConst2
is legal C++.
I've found the following two existing questions:
- Is it legal to modify any data pointer through a void **
- Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"
However, both of them don't seem to quite answer my question. The first one deals with casting any T**
to a void **
, which is not what I'm doing; I'm just casting away constness. The second one asks why it's a compile error to convert a void **
to a const void **
, but not whether interpreting the memory of a void *
as a const void *
and vice-versa (without actually overwriting a const object) would be a violation of the aliasing rules.