1

This code doesn't compile:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

s_t s;
c_s_t *c_s = &s;
ibug@ubuntu:~ $ g++ -fsyntax-only t.cpp
t.cpp:10:15: error: cannot convert ‘s_t*’ to ‘c_s_t*’ in initialization
 c_s_t *c_s = &s;
               ^

However this one compiles perfectly:

int a, *pa = &a, **ppa = &pa, ***pppa = &ppa;
const int * const * const * const cpcpcpa = pppa;

I understand that a pointer that is more CV-qualified at any level can point to a less CV-qualified object at any level, but why isn't it the same for structures?


The above problem statement is a MCVE of a more complex problem, where my friend was trying to convert pointers between t_s_t<T> and t_s_t<const T>, where t_s_t is a template struct type with one template parameter typename, and T is an arbitrary type.

iBug
  • 35,554
  • 7
  • 89
  • 134

4 Answers4

4

The reason is that s_t and c_s_t are different types.

Even if you define c_s_t as:

struct c_s_t {
    int a; // <-- non-const!
};

Then:

s_t s;
c_s_t *c_s = &s;

It is still not going to work.

JFMR
  • 23,265
  • 4
  • 52
  • 76
1

Type error is your problem you are simply trying to assign the wrong types.

This would work:

s_t s;
s_t *c_s = &s; //types are matching
KostasRim
  • 2,053
  • 1
  • 16
  • 32
0

Those two are different structs, and they're not convertible. The fact that they have the same members is irrelevant, and even if you removed const from c_s_t, it wouldn't change anything.

Your other example works because you're applying modifiers to one type. For example, this is perfectly legal:

struct s_t {
    int a;
};

s_t s;
const s_t const* cs = &s;
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
0

I understand that a pointer that is more CV-qualified at any level can point to a less CV-qualified object at any level

This isn't actually true, at least not in the way you've described. Only the topmost CV-qualifier can be added arbitrarily (and, of course, a CV-qualifier on the pointer itself!), which is the case in both C and C++.

Here's a counter-example for the "any level" notion, taken straight from [conv.qual/3] in the current draft standard:

[ Note: If a program could assign a pointer of type T** to a pointer of type const T** (that is, if line #1 below were allowed), a program could inadvertently modify a const object (as it is done on line #2). For example,

int main() {
  const char c = 'c';
  char* pc;
  const char** pcc = &pc;       // #1: not allowed
  *pcc = &c;
  *pc = 'C';                    // #2: modifies a const object
}

— end note ]

Anyway, then you ask:

but why isn't it the same for structures?

Sure, you can point a const T* to a T, but that's not what you're doing. This rule doesn't apply recursively. Classes can hold more than one member so your approach just doesn't work in general (and there's no need for a special rule for single-member classes).

In this particular case, the two classes are layout-compatible, so I'd expect a reinterpret_cast to appear to work most of the time:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

int main()
{
   s_t s;
   c_s_t *c_s = reinterpret_cast<c_s_t*>(&s);
}

(live demo)

However, it appears that aliasing on the merits of layout-compatibility is not actually well-defined so ultimately you're better off rethinking your design.

tl;dr: Different types are different types.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Nope. In C++ you can assign `int **ppi; int * const * pcpi = ppi;`. In C however only top level CV-qualification can be added. – iBug Oct 05 '18 at 09:23
  • @iBug `ppi*` is invalid syntax, and I've directly quoted the standard to illustrate what I'm talking about. What _you've_ shown is adding `const` at the top pointee level, not "any" level which is what you claimed is possible! – Lightness Races in Orbit Oct 05 '18 at 09:23
  • I know why "less qualified" pointers can't point to "more qualified" object, and that's not what I'm asking about. – iBug Oct 05 '18 at 09:24
  • @iBug I don't think you're right about C, either: http://coliru.stacked-crooked.com/a/e76a9077035bfa15 (though that _could_ be a GCC bug or extension - I haven't scoured the standard) – Lightness Races in Orbit Oct 05 '18 at 09:25
  • [6.3.2.3/2](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf): _For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal._ So the same as in C++. – Lightness Races in Orbit Oct 05 '18 at 09:29
  • Where am I wrong about C? I said top-level CV-qual *can* be added, while *non*-top-level *can't* be added. But in C++, *non-top-level* qual ***can*** be added. – iBug Oct 05 '18 at 09:29
  • @iBug I literally just quoted the passage from the C11 working draft that says the opposite of what you claim. I just checked C90 too and the same wording is in there. – Lightness Races in Orbit Oct 05 '18 at 09:30
  • @iBug Instead of arguing over whether the standard wording is wrong on an unrelated point, let's discuss whether the solution I provided fixes your problem. – Lightness Races in Orbit Oct 05 '18 at 09:31
  • OK then, your solution of using `reinterpret_cast` seems valid. – iBug Oct 05 '18 at 09:33
  • I don't think there is anything in the standard about layout compatible structures right? I am sure this would work in all occasions, is it actually well defined? https://stackoverflow.com/questions/21956354/can-i-legally-reinterpret-cast-between-layout-compatible-standard-layout-types – Fantastic Mr Fox Oct 05 '18 at 10:12
  • @FantasticMrFox Dang! – Lightness Races in Orbit Oct 05 '18 at 10:23