检测Sfinae POD类型的第一成员

Detecting first member of POD types for SFINAE

本文关键字:成员 类型 Sfinae POD 检测      更新时间:2023-10-16

给定一些与此类似的POD结构:

struct StandardHeader
{
    uint32_t field1;
    uint32_t field2;
};
struct TypeA
{
    StandardHeader Header;
    uint8_t field3;
};
struct TypeB
{
    StandardHeader Header;
    uint16_t field4;
};

我想编写一种类型特征(或最终可以在static_assertstd::enable_if中使用的类似特征或以其他方式禁用模板方法(,该方法可以检测到StandardHeader字段的存在为标准布局类型的第一个成员 - IE。因此reinterpret_cast<StandardHeader*>(&instance)是安全的。

(从本质上讲, is-a 基本类型检查,除了类型必须是Pods,如果我使用实际的C 继承,那将是正确的。(

我能够写一些使用检测成语来验证该类型是标准布局的东西,并且具有正确类型的Header成员:

template<typename, typename = std::void_t<>>
struct HasStandardHeader : std::false_type {};
template<typename T>
struct HasStandardHeader<T,
        std::void_t<decltype(std::declval<T>().Header)>>
    : std::conditional_t<
        std::is_standard_layout_v<T> &&
        std::is_same_v<decltype(std::declval<T>().Header), StandardHeader>
    , std::true_type, std::false_type> {};

以上部分有效,但没有验证该字段是第一个。

我试图沿此表达式添加一些东西以检测到这一点,但它不起作用:

static_cast<uint8_t*>(&static_cast<T*>(0)->Header) - static_cast<uint8_t*>(0)) == 0

(涉及调用constexpr bool方法的其他失败尝试,但可悲的是,这些尝试在conditional_t中似乎无效。或者至少在使用&或其他情况下不有效。(

理想情况下,我更喜欢只能检测到正确类型的字段的东西,甚至不需要将其命名为Header。那可能吗?并且有更好的改写上述方法吗?

如前所述,最终目标是制作此方法:

template<typename T>
bool Process(T& data, size_t len);

如果T不是具有正确的第一个成员的POD类型,则消失或static_assert


edit :看起来我可能已经过度复杂了一点。添加此表达式使其正常工作:

offsetof(T, Header) == 0

但是现在:

  • 有更好的写作方法吗?
  • 是否可以写这篇文章,以使我们不需要该字段被称为Header

只需检查成员的偏移。

template<typename, typename = std::void_t<>>
struct HasStandardHeader : std::false_type {};
template<typename T>
struct HasStandardHeader<T,
        std::void_t<decltype(std::declval<T>().Header)>>
    : std::conditional_t<
        std::is_standard_layout<T>::value &&
        std::is_same<decltype(std::declval<T>().Header), StandardHeader>::value &&
        offsetof(T, Header) == 0
    , std::true_type, std::false_type> {};

在Godbolt上进行了一些测试。