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.
CodePudding user response:
Yes, it is legal.
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
- the dynamic type of the object [...]
T*
and const T*
are similar:
Two types
T1
andT2
are similar if they have cv-decompositions with the same n such that corresponding Pi components are either the same or one is "array of Ni" and the other is "array of unknown bound of", and the types denoted byU
are the same.