Xeren Narcy Xeren Narcy - 23 days ago 13
C++ Question

C++ Compiler behavior with re-interpreting template parameter constants

While working on an RAII-style guard object, I ended up encoding some of the guard state in a template parameter. This seems reasonable if, for instance, you want a recursive / nested guard object that's aware of how many levels deep it is but without space overhead (being pedantic, I know) or to eliminate some runtime overheads. This turned into an academic curiosity though...

Something like this would be an example:

template <unsigned depth>
class guard {
unsigned get_depth() const {return depth;}
};

guard<2> g2;
std::cout << reinterpret_cast< guard<5>* >( &g2 )->get_depth(); // works? crazy? useful?


I cannot for the life of me think of a legitimate reason to do this, but it got me thinking if this is legal C++ and just how the compiler ought to handle something like this (if it can at all) or if it's just silly through and through.

I assume because the cast target needs to be known at compile time, that the relevant template is instantiated for the cast. Has anyone found something like this useful, assuming it does work and has uses at all, and if so where could this be utilized?

The general question I guess is can
reinterpret_cast
alter constant template parameters?
If so is this just a type hack (for want of a better term) and
g2
in this case would always return
2
(after casting)? Or should it return
5
(after casting)?

Answer

Your code exhibits undefined behavior. You're not allowed to access objects of different types through a glvalue of different type (sans the whitelist in 3.10.10). And types stamped from the same template, but with different parameters are definitely unrelated.

N4140 ยง 3.10 [basic.lval]/10

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,

  • a cv-qualified version of the dynamic type of the object,

  • a type similar (as defined in [conv.qual]) to the dynamic type of the object,

  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,

  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,

  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),

  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,

  • a char or unsigned char type.

Reasoning about UB is usually pointless, but if you really want to, you can notice that your get_depth is as good as a static member function and it behaves as such.

Comments