【问题标题】:Nested C Array Struct Alignment in ClangClang中的嵌套C数组结构对齐
【发布时间】:2023-03-27 09:40:01
【问题描述】:

我在使用 Clang 为 x64 编译的 C++17 中有一个小的内存问题。由于我使用低级内存副本到专用硬件,因此我确实依赖于按预期对齐的内存。我最近从 Visual Studio 迁移到 Clang,遇到了以下问题:

见以下代码:

struct ContainerRoot{};
template<size_t t_size, class t_type>
struct Container : public ContainerRoot
{
    t_type contents[t_size];
};

static_assert(sizeof(Container<1, double>) == 8);           //This works as expected
static_assert(sizeof(Container<4, double>) == 32);          //This works as expected
static_assert(sizeof(Container<16, double>) == 128);        //This works as expected
static_assert(sizeof(Container<4, Container<4, double>>) == 128); //This fails. 

在上面的例子中 sizeof(Container) 实际上是 136(额外的 8 个字节)。我的问题是为什么要添加额外的 8 个字节,我可以避免吗?我很想使用 Clang 编译器,但如果没有解决方法,可能不值得重写所有围绕将此数据发送到专用硬件的代码。

到目前为止,我已经使用 std::is_polymorphic>>() 进行了检查,只是为了确认没有将一些虚拟查找表添加到类中。

【问题讨论】:

  • Container&lt;Container&lt;double&gt;&gt; 不应该编译:Container 需要两个模板参数。
  • 对不起,我试图做一个简单的例子。实际上没有测试编译。稍微整理了一下示例。
  • 鉴于Container 实际上继承自某些东西:RTTI 是否已激活?如果是这样,编译器可能会添加一个空的 vftable,只是为了知道它处理的是哪种类型。还解释了不同编译器的不同行为。奇怪的是:在这种情况下,我预计会有更多的开销。
  • 有趣:即使 RTTI 被停用也会失败。但请注意:sizeof(Container&lt;4,double&gt;[4]) == 128。如果继承被删除,也可以工作。似乎是空基类优化的问题(en.cppreference.com/w/cpp/language/ebo)。

标签: c++ templates memory clang padding


【解决方案1】:

引用en.cppreference.com:

任何对象或成员子对象的大小 [...] 必须在 即使类型是空类类型(即类或 结构没有非静态数据成员),以便能够 保证相同类型的不同对象的地址是 总是与众不同。

此约束不适用于空基(空基优化)。

然而,en.cppreference.com 也指出:

如果有空基类之一,则禁止空基优化 也是第一个非静态数据类型的类型或基数 成员,因为需要两个相同类型的基础子对象 具有不同地址的对象表示最多 派生类型。

不幸的是,当Container&lt;4, Container&lt;4, double&gt;&gt; 作为第一个成员(类型为Container&lt;4, double&gt;)和Container&lt;4, Container&lt;4, double&gt;&gt; 都继承自ContainerRoot 时,情况就是这样。

因此,我假设 MSVC 在这种情况下实际上是错误的,并且过早地应用了它不能做的优化。

【讨论】:

  • @cigien 不会是第一次。
  • 这对我来说很有意义。不确定我是否理解背后的原因:“因为相同类型的两个基本子对象需要在最派生类型的对象表示中具有不同的地址。”。在这种情况下,基类的原因除了提供某些帮助函数之外没有其他目的。例如:'template typename std::enable_if<:is_base_of t_type>::value, t_type>::type abs(const t_type& value){...};'
  • @TylerParke 我也不完全明白这一点,但问题似乎是(ContainerBase*)(container) 和(ContainerBase*)(container.contents[0]) 共享相同的地址和相同的类型。我相信当content 调用ContainerBase 的方法时会出现问题,例如contents[0].baseMethod();。当该方法被调用时,它不清楚它对哪个对象进行操作以及它应该如何区分。它应该作用于封闭的Container 对象还是作用于contents[0]?两者具有相同的地址并且都调用相同的函数。
  • 啊,现在我很好奇使用上面的指针示例在 VS 中会发生什么。这实际上完全有道理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-03
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
相关资源
最近更新 更多