是否可以根据其数据成员的类型确定类型的大小
Is it possible to determine the size of a type from the types of its data members
问题:
考虑以下类型:
struct S {
std::uint8_t a;
std::uint32_t b;
};
是否可以单独从std::uint8_t
和std::uint32_t
确定sizeof(S)
?
上下文:
我正在开发一个代码,其中 S
实际上是一个模板类型,比这个用于说明的模板类型更冗长,所以我想定义一个特征 class 来一般地确定一个模板的大小类型来自其数据成员的类型。到目前为止,我有两个想法:
- 使用数据成员对齐
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof...(Ts) * std::max(alignof(Ts)...);
- 使用
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));
这是确定类型大小的正确方法吗?
是的。如果对象成员的类型和顺序已知,并且所有成员都具有相同的访问说明符(例如 public
),如果对象缺少 vtable,则可以使用 和 在助手 class 中重建对象的结构并使用其大小:
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
灵感来自。
问题:
考虑以下类型:
struct S {
std::uint8_t a;
std::uint32_t b;
};
是否可以单独从std::uint8_t
和std::uint32_t
确定sizeof(S)
?
上下文:
我正在开发一个代码,其中 S
实际上是一个模板类型,比这个用于说明的模板类型更冗长,所以我想定义一个特征 class 来一般地确定一个模板的大小类型来自其数据成员的类型。到目前为止,我有两个想法:
- 使用数据成员对齐
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof...(Ts) * std::max(alignof(Ts)...);
- 使用
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));
这是确定类型大小的正确方法吗?
是的。如果对象成员的类型和顺序已知,并且所有成员都具有相同的访问说明符(例如 public
),如果对象缺少 vtable,则可以使用 和 在助手 class 中重建对象的结构并使用其大小:
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
灵感来自