0

I have the following code that does not compile with gcc 10.2.1 :

struct Bar {
    unsigned char *m_a;
    unsigned char m_b[1];
};

int main()
{
    Bar bar;
    const Bar &b = bar;

    void *p1 = b.m_a; // Ok
    void *p2 = b.m_b; // Error

    return 0;
}

The compiler error is :

error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]

I can fix this by using either void *p2 = (void *)b.m_b; or void *p2 = const_cast<unsigned char *>(b.m_b); however, constness of members does not seem to be treated the same by the compiler.

I guess there is an "extra-check" for the array and not with the pointer but why is that ?

Thank you.

Fryz
  • 2,119
  • 2
  • 25
  • 45
  • In the case of pointer, then the value of the pointer becomes const, and not what it points to. You can modify the unsigned char that it points to, but you cannot make it point to another unsigned char location. When objects are `const`ed, only the top level qualifier of the members will implicitly also be `const`ed – Mestkon Aug 26 '22 at 11:01
  • Use `const void*`. For example `const void *p2 = b.m_b;`. See [demo](https://onlinegdb.com/ChiZjPsko) – Jason Aug 26 '22 at 11:03
  • It is the same situation as `const int a[1]; int* p = a;`, and you probably understand why that is bad. – molbdnilo Aug 26 '22 at 11:09
  • *why is that?* Because in C++ `const`-ness is not transitive in most situations. – Eljay Aug 26 '22 at 12:16

3 Answers3

4

Having a const struct adds const to all the members.

Adding const to unsigned char * gives you unsigned char * const (i.e., the pointer cannot be changed to point to anything else, but you can change the value of what is pointed to). This can be cast to void *, since that is also a pointer to non-const.

Adding const to unsigned char[1] gives you const unsigned char[1] (A const array of T is actually an array of const T). This can decay into a const unsigned char * pointer, which can be cast to a const void *, but not a void * without casting away the const. This is because the elements of the array cannot be modified, unlike the pointed-at object with the first member.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • Thanks for pointing this out. This question https://stackoverflow.com/questions/34488559/pointer-to-array-with-const-qualifier-in-c-c also helps. – Fryz Aug 26 '22 at 12:56
0
b.m_a

This is an unsigned char *. It is happy to be converted to a void *.

b.m_b

This is a const unsigned char *. That's how arrays work in C and C++: the name of the array decays to a pointer to the first value in the array. This array is const, therefore this becomes a const unsigned char *, and you can't convert it to a void *.

Note that you'll get the same error if you attempt to convert &b.m_a to a void *.

b.m_a itself is const, but it's not pointing to a const object. These are two very different things: a const pointer and a pointer to a const.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
0

The expression b is of type const Bar meaning the data member m_a is unsigned char *const and m_b is const unsigned char [1].

Now, the important thing to note is that:

a pointer to any nonconst type can be converted to void*, and a pointer to any type can be converted to a const void*

(emphasis mine)Source

This means that b.m_a can be implicitly converted to a void* and b.m_b can be implicitly converted to const void* and not void*.

Thus to solve your problem, you should add a low-level const to p2 as shown below:

vvvvv-------------------->low level const added
const void *p2 = b.m_b; // works now

Demo

Jason
  • 36,170
  • 5
  • 26
  • 60