【问题标题】:Is it possible to determine the size of a type from the types of its data members是否可以根据数据成员的类型确定类型的大小
【发布时间】:2023-04-10 09:08:01
【问题描述】:

问题:

考虑以下类型:

struct S {
    std::uint8_t a;
    std::uint32_t b;
};

是否可以单独从std::uint8_tstd::uint32_t 确定sizeof(S)

上下文:

我正在开发一个代码,其中S 实际上是一个模板类型,比这个用于说明目的的模板类型更详细,所以我想在定义一个特征类时从它的数据成员的类型中通用地确定一个类型的大小.到目前为止,我有两个想法:

  1. 使用数据成员的对齐方式
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof...(Ts) * std::max(alignof(Ts)...);
  1. 使用std::tuple
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof(std::tuple<Ts...>);

使用这两种方法似乎都成功了,虽然我不太愿意使用第二种方法,因为std::tuple 的实现不能保证它的大小:

static_assert(aligned_sizeof_all_v<std::uint8_t, std::uint32_t> == sizeof(S));

这是确定类型大小的正确方法吗?

【问题讨论】:

  • 当你可以问sizeof给你类的大小时,为什么你需要从它的成员中确定类的大小?除了知道班级的规模之外,别无他法。
  • 通常您无法知道尺寸是多少。一旦涉及到虚函数,情况就更是如此。 C++ 已经有很多关于函数推导的东西,所以你最好问一下。
  • 用你的aligned_sizeof_all_v 试试struct S { uint8_t a, b; std::uint32_t c; };。我认为它不会提供正确的尺寸。
  • @SepiaColor 它对我不起作用:godbolt.org/z/S3wjkz。或者,我没有正确理解您的问题。我在这种情况下看到的问题是aligned_sizeof_all_v 是“对称的”,而结构的大小可能取决于其成员的顺序。

标签: c++ sizeof typetraits memory-alignment stdtuple


【解决方案1】:

是的。如果对象成员的类型和顺序已知,并且所有成员具有相同的访问说明符(例如public),并且如果对象缺少 vtable,您可以在其中重建对象的结构一个辅助类并使用它的大小:

template<typename... Ts>
struct struct_traits {
    static constexpr std::size_t size() {
        return sizeof(struct_builder<Ts...>);
    }

private:
    template<typename First, typename... Rest>
    struct struct_builder : struct_builder<Rest...> {
        First v;
    };
    template<typename First>
    struct struct_builder<First> {
        First v;
    };

    // Below: addition to work with structures declared with alignas().
public:
    template<std::size_t alignas_>
    static constexpr std::size_t size() {
        return sizeof(aligned_struct_builder<alignas_, Ts...>);
    }

private:
    template<std::size_t alignas_, typename First, typename... Rest>
    struct alignas(alignas_) aligned_struct_builder : aligned_struct_builder<alignas_, Rest...> {
        First v;
    };
    template<std::size_t alignas_, typename First>
    struct alignas(alignas_) aligned_struct_builder<alignas_, First> {
        First v;
    };
};

使用示例(试一试on Godbolt):

struct S
{ 
    uint8_t a, b;
    uint32_t c;
};

static_assert(struct_traits<uint8_t, uint8_t, uint32_t>::size() == sizeof(S)); // True! 8 == 8
static_assert(struct_traits<uint8_t, uint32_t, uint8_t>::size() == sizeof(S)); // Bad: 12 != 8

针对alignas-ed 结构(on Godbolt)使用:

struct alignas(8) S
{ 
    uint8_t a;
    uint32_t c;
    uint8_t b;
};

static_assert(struct_traits<uint8_t, uint32_t, uint8_t>::size<8>(), sizeof(S)); // True: 16 == 16

灵感来自this SO post

【讨论】:

  • 这假定布局兼容类型具有相同的大小。按照this的说法,可能无法保证?
  • @SepiaColor: struct_builder 不一定要与布局兼容;我认为这段代码依赖于对所有结构使用相同对齐决策的编译器。如果编译器以与struct B { char q; int x; }; 相同的方式填充struct A { char c; int n; }; 的成员,那么A 和B 的大小将是相同的。我会说这个假设是安全的。
  • @MooingDuck:双重打击:引入 vtable 会影响结构的大小,以及使用 alignas。至少可以通过添加非类型模板参数来解释后者,再次更新我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多