constexpr具有指向成员数据的指针的Offsetof

时间:2018-04-28 20:00:45

标签: c++

在这个问题因为重复而被驳回之前,大多数(如果不是全部)问题已经接受了过时/未定义行为解决方案的答案。

问题:

有没有办法让编译时,指向成员数据的指针偏移:

  • 不依赖于未定义的行为(nullptr事物)
  • 适用于最新的gcc版本(编译器资源管理器中的gcc trunk
  • offsetof的工作方式相同(或类似地,与提议的代码一样)。

我不在乎:

  • 非标准布局类型
  • msvc兼容性。 (它可能很好,但可选)
  • 具有特定编译器的特殊情况

问题:

使用GCC的主干版本,以下技巧(hack)不起作用:

#include <cstdint>
#include <cstddef>

namespace detail
{
    // The union stuff is for CLang to avoid creating warnings
    template<typename T, auto MPtr>
    struct offsetof_ptr_helper
    {
        union helper_t { int i = 0;  T value; };
        static constexpr helper_t v = {};
        static constexpr size_t sz = sizeof
        (uint8_t[
            (uint8_t *)&(v.value.*MPtr) -
            (uint8_t *)&v.value
        ]);
    };
}

template<typename T, auto MPtr>
static constexpr size_t offsetof_ptr()
{
    return detail::offsetofptr_helper<T, MPtr>::sz;
}

size_t f()
{
    struct X { char c[10]; int a; };
    return offsetof_ptr<X, &X::a>();
}

要明确的是,这是一个使用C ++ 98功能的黑客,这并不是一个真正的惊喜,它的支持被最新的编译器删除。它适用于clang(使用-std=gnu++17标志)和gcc(使用标志-std=c++17包括/ up-to g ++ 7.3)。

1 个答案:

答案 0 :(得分:0)

如果你跳转到 VS 中 offsetof 的定义,它会给你这样的:

#define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))

稍微修改以匹配您的问题:

template<typename T, auto MPtr>
static constexpr size_t offsetof_ptr()
{
    return ((::size_t) & reinterpret_cast<char const volatile&>((((T*)0)->*MPtr)));
}

size_t f()
{
    struct X { char c[10]; int a; };
    return offsetof_ptr<X, &X::a>();
}

这在 GCC 和 MSVC 上编译。 (由于模板自动参数,它需要 C++17。)