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.