为何自定义Tuple大小超出预期?如何启用空基类优化?
问题原因
你的Tuple实现中,空类Nil是作为成员变量存储在Tuple<Nil>中的,这导致Tuple<Nil>不是空类——它包含一个Nil类型的成员,哪怕Nil是空类,该成员也会占据1字节(C++标准要求非空类的大小至少为1)。当Tuple<int*, Nil>继承自Tuple<Nil>时,基类的1字节加上自身int*的8字节,由于内存对齐要求(int*的对齐边界是8字节),总大小被填充到16字节,因此测试失败。
空基类优化(EBO)的核心生效条件是:基类必须是空类(无自身非静态数据成员、无虚函数/虚基类),且派生类的非静态成员类型与基类不同。你的实现中,空类被作为成员而非基类存储,因此无法触发EBO。
解决方案
调整Tuple的实现,将每个元素通过支持EBO的包装器存储为基类,而非成员。这样空类元素会被作为空基类处理,从而触发EBO优化。
修改后的完整代码如下:
#include <type_traits> #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include <doctest/doctest.h> // 支持EBO的元素包装器 template <typename T, bool = std::is_empty_v<T> && !std::is_final_v<T>> struct TupleElement : T { constexpr TupleElement(const T& val) : T(val) {} constexpr T& get() noexcept { return *this; } constexpr const T& get() const noexcept { return *this; } }; // 非空类/基本类型,用成员存储 template <typename T> struct TupleElement<T, false> { constexpr TupleElement(const T& val) : value(val) {} constexpr T& get() noexcept { return value; } constexpr const T& get() const noexcept { return value; } private: T value; }; // Tuple类模板实现 template <typename...> struct Tuple; template <> struct Tuple<> {}; template <typename Head, typename... Tail> struct Tuple<Head, Tail...> : Tuple<Tail...>, TupleElement<Head> { constexpr Tuple(const Head& head, const Tail&... tail) : Tuple<Tail...>(tail...), TupleElement<Head>(head) {} }; struct Nil {}; TEST_CASE("test size") { using T0 = Tuple<>; CHECK(sizeof(T0) == 1); using T1 = Tuple<double, std::string, int, char>; struct Foo { double d; std::string s; int i; char c; }; CHECK(sizeof(T1) == sizeof(Foo)); using T2 = Tuple<int*, Nil>; CHECK(sizeof(T2) == sizeof(int*)); using T3 = Tuple<int*, Nil, Nil>; CHECK(sizeof(T3) == sizeof(int*)); }
说明
- TupleElement包装器:自动判断元素类型,空类直接继承(作为空基类),非空类/基本类型用成员存储,兼顾EBO优化和基本类型的存储需求。
- Tuple递归结构:每个
Tuple<Head, Tail...>继承自Tuple<Tail...>和TupleElement<Head>,空类元素对应的TupleElement是空基类,触发EBO优化,其大小会被编译器完全忽略。 - 测试效果:
Tuple<int*, Nil>和Tuple<int*, Nil, Nil>的大小会等于sizeof(int*),仅保留非空元素的大小(满足对齐要求)。
内容的提问来源于stack exchange,提问作者bobeff
相关产品推荐
相关产品推荐

