static_assertupcast是否需要调整指针

static_assert whether pointer adjustment is required for an upcast

我想执行编译时检查,以确定从派生类型到基类型的向上转换是否需要指针调整。我从 type_traits 中看不到任何可以使用的东西。这能做到吗?

这是一个如何在运行时完成此操作的示例。请注意,clang 甚至会警告第一个 printf 永远不会打印“yes”,第二个永远不会打印“no”,因此它会将这些调整值评估为常量。问题是,如何在编译时访问这些值?

#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;
}

这称为指针互换性。有一个特质std::is_pointer_interconvertible_base_of which unfortunately isn't implemented yet in gcc and clang. See also the paper and issue。指针可互换类型具有相同的地址,它们的指针可以在 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

这是一个不需要 C++20 的可能实现:

template< class Base, class Derived >
struct is_pointer_interconvertible_base_of
{
private:
  // Note that this doesn't seem to be ODR-used in the end, so an actual
  // instance isn't needed. Clang generates a warning about needing to
  // instantiate it, but linking succeeds nevertheless
  static Derived derived;

public:
  // Suppress that clang warning :)
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundefined-var-template"
#endif
  // TODO: add std::is_base_of check etc
  static constexpr bool value = ( ( void const * ) &derived ==
                                  ( Base const * ) &derived );
#ifdef __clang__
#pragma clang diagnostic pop
#endif
};

struct A
{
  int a;
};

struct B
{
  int b;
};

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

static_assert( is_pointer_interconvertible_base_of< A, C >::value,
               "pointer adjustment isn't expected when casting from C to A" );
static_assert( !is_pointer_interconvertible_base_of< B, C >::value,
               "pointer adjustment is expected when casting from C to B" );

int main()
{
  return 0;
}

似乎适用于 clang 和 gcc。 Clang 生成了需要实例化derived 的警告,但最后好像不是这样。这里没有处理 Derived 实际上不是从 Base 派生的情况,但是很容易添加 std::is_base_of 检查和其他需要的检查。不知道这是否符合标准,但在更广泛地提供 C++20 支持之前它可能会有所帮助。