7

Consider the following type:

template<typename T> struct View
{
    T*     data;
    size_t size;
};

Is it valid to cast from: View<T>& To View<const T>&?

Does it invoke undefined behaviour?

I suspect the cast will work as intended on all compilers but will be technically undefined behaviour according to the standard due to the strict aliasing rule.

8
  • 1
    The strict aliasing rule in C++ says that, except for a few exceptions, an object of one type cannot be accessed through a pointer to a different type. However, this rule has specific allowances for const and volatile qualified types.
    – Dmitrii
    Commented Jul 5 at 12:22
  • 1
    This is Undefined Behaviour. But you can implement a converting constructor or conversion operator to allow converting from View<T> to View<const T>. Commented Jul 5 at 12:32
  • 4
    @Dmitrii Converting a foo<T>& to foo<const T>& is not exempt from strict aliasing rules, they are unrelated types. You could cast data individually to const T* but that isn't the same as casting a reference to the whole struct. Commented Jul 5 at 12:34
  • 1
    We notice that shared_ptr has special casts that cast its inner pointer instead of the outer pointer.
    – BoP
    Commented Jul 5 at 13:24
  • 2
    This is formally undefined behavior, but I have seen this exact trickery in many C++ programs and it "seems to work" on every compiler and architecture. If this is a language lawyer question (i.e. you are only interested in whether this is formally defined) then the answer is trivial. However, if you are looking for a counter example where it fails, it's a much harder question. You should clarify which of these two you care about, otherwise the question will likely just attract a lot of pointless argumentation. Commented Jul 5 at 20:26

1 Answer 1

8

View<T> and View<const T> are unrelated types and therefore casting a reference from the first to the second violates the strict aliasing rules and causes UB (undefined behavior).

It is true that is it valid to cast a T* to const T* (because these are related types), but this is not relevant when casting a reference to a class containing such members (View).

It might work as you expect on your compiler, but it is still undefined behavior by the standard and so I would not advise to rely on it.

As @FrançoisAndrieux commented above, you can cope with it by adding a converting constructor or conversion operator to allow converting from View<T> to View<const T>.

Not the answer you're looking for? Browse other questions tagged or ask your own question.