Search code examples
c++inheritancemultiple-inheritance

static_assert whether pointer adjustment is required for an upcast


I'd like to perform a compile-time check for whether an upcast from a derived type to a base type requires a pointer adjustment. I'm seeing nothing I could use from type_traits. Can this be done?

Here's an example of how this might be done at runtime. Note that clang even warns that the first printf would never print "yes" and the second one would never print "no", so it does evaluate those adjustment values to constants. Question is, how to access those values at compile time?

#include <stdio.h>

struct A
{
  int a;
};

struct B
{
  int b;
};

struct C: A, B
{
  int c;
};

int main()
{
  C c;

  // Prints "no"
  printf( "Is pointer adjustment required when casting from C to A? %s\n",
          ( ( char * ) ( A * ) &c - ( char * ) &c ) ? "yes" : "no" );

  // Prints "yes"
  printf( "Is pointer adjustment required when casting from C to B? %s\n",
          ( ( char * ) ( B * ) &c - ( char * ) &c ) ? "yes" : "no" );

  // Can we have those values as constexpr though?

  return 0;
}

Solution

  • This is called pointer interconvertibility. There is a trait std::is_pointer_interconvertible_base_of which unfortunately isn't implemented yet in gcc and clang. See also the paper and issue. Pointer interconvertible types have the same address and their pointers can be casted between with reinterpret_cast.

    #include <type_traits>
    
    #ifdef __cpp_lib_is_pointer_interconvertible
        // Prints "no"
        printf( "Is pointer adjustment required when casting from C to A? %s\n",
          std::is_pointer_interconvertible_base_of_v<A, C> ? "no" : "yes" );
    
        // Prints "yes"
        printf( "Is pointer adjustment required when casting from C to B? %s\n",
          std::is_pointer_interconvertible_base_of_v<B, C> ? "no" : "yes" );
    #endif