C++ Logo


Advanced search

Casting pointers in constant evaluation

From: Keenan Horrigan <friedkeenan_at_[hidden]>
Date: Wed, 06 Oct 2021 09:17:46 +0000
In constant expressions, it's possible to static_cast between certain pointers, e.g. between a pointer to a derived object and a pointer to a base object.

But, notably, you cannot reinterpret_cast in constant expressions. I've run into this before, but generally I've been able to get around this constraint by using std::bit_cast. However, if e.g. you want to make a constexpr (and therefore compliant) implementation of std::addressof, you need compiler magic, since a pure language implementation, as far as I can tell, requires reinterpret_cast. This is where I start to not like this constraint: reinterpret_cast is disallowed in constant expressions even when it's not undefined behavior, i.e. when you cast to a pointer to an aliasing type. Why is this? Did the committee not want to put too much burden on compilers to keep track of when the use of reinterpret_cast would be UB? That's all I can think of, as surely in constant evaluation objects still have their underlying data, as evidenced by std::bit_cast? Or is it because in constant evaluation pointers act more like rebindable references rather than addresses to memory? I could certainly understand disallowing casting to e.g. std::uintptr_t for that reason, but not so much pointers to aliasing types.

Another fun fact about casting pointers in constant expressions: static_casting from cv-qualified void pointer is disallowed. For this I think I can see why it might be desirable to do this, as a void pointer erases the type of the pointed-to object, but I disagree that this should be disallowed. There are in fact non-UB uses of casting to and from void pointers. For instance, as far as I can tell, casting a pointer to a union to a void pointer and then casting to a pointer to one of the union's elements is not UB. This is useful for a constexpr implementation of std::allocator in pure C++ as you can use a union to have an uninitialized object.

For example, see [this godbolt link](https://godbolt.org/z/cP7bofYd7) where I allocate a union containing an int and then cast its pointer to a void pointer and then to an int pointer. Currently, gcc is non-conforming/bugged and allows this code in constexpr, and as far as I can tell, it still detects all the non-UB uses of such a thing, going past the bounds, using the value of an inactive element, etc. Clang however, rejects the code. But if you remove the constexpr and static_assert, clang computes everything as-expected on all optimization levels. So again I have to wonder, why does the standard disallow this?

Would it be feasible for a paper to change either of these things?

Received on 2021-10-06 04:17:59