【问题标题】:Is the address of a std::array guaranteed the same as its data?std::array 的地址是否保证与其数据相同?
【发布时间】:2021-11-28 16:48:05
【问题描述】:

std::array 是...(引自cppreference):

此容器是一种聚合类型,其语义与将 C 样式数组 T[N] 作为其唯一非静态数据成员的结构相同。

这是否意味着数组的地址总是与其第一个元素的地址相同,即data()?

#include <array>
#include <iostream>

int main()
{
    std::array<int,6> x{};
    std::cout << &x << "\n";
    std::cout << x.data();
}

可能的输出:

0x7ffc86a62860
0x7ffc86a62860

如果是,这有什么用吗?以下是否允许?

int* p = reinterpret_cast<int*>(&x);
for (int i=0;i<6;++i){ std::cout << p[i]; }

【问题讨论】:

  • 你为什么要做reinterpret_cast&lt;int*&gt;(&amp;x) 而不仅仅是x.data()?
  • C 有一个规则,任何struct 的地址都等于它的第一个成员的地址,并且指向一个的指针可以安全地转换为另一个。 C++ 对classes 有相同的规则吗?我不太确定在标准中的哪个位置。
  • 实际上我没有在标准中看到“仅非静态数据成员”;该标准似乎并未排除其他成员。 cppreference 弥补了吗?
  • @NateEldredge 静态成员,无论是数据还是方法或其他任何东西,都不会占用对象分配的存储空间中的任何空间。只有非静态数据成员才会这样做。
  • 您正在假设标准定义的类的内部布局。与这些事情一样,该标准没有明确说明内部布局应该是什么(我也看不到任何提及它只有一个数据成员)。

标签: c++ language-lawyer stdarray


【解决方案1】:

从技术上讲,std::array 对象可能在开头有填充,在这种情况下,数据将位于比std::array 对象更高的地址。只有标准布局类才能保证对象本身与第一个非静态数据成员具有相同的地址。

标准中不能保证std::array&lt;T, N&gt; 是标准布局,即使T 是int 或类似的东西。 std::array&lt;T, N&gt; 的所有合理实现都将有一个 T[N] 类型的非静态数据成员,没有虚函数,并且最多只有一个没有虚拟基类的继承链,这意味着它们只要T 本身是标准布局,它就是标准布局。另外,即使T 不是标准布局,编译器也不太可能在std::array 对象的开头插入填充。

因此,虽然假设 std::array&lt;T, N&gt; 对象与它包含的第一个 T 对象具有相同的地址是不可移植的,但在实践中基本上可以保证。您可以添加 static_assert(sizeof(std::array&lt;T, N&gt;) == sizeof(T[N])); 以确保,如果有人试图在并非如此的奇异实现上构建您的代码,他们会知道它不受支持。

【讨论】:

  • 标准确实说std::array 是一个聚合。那么,这是否意味着标准布局?
  • @RemyLebeau 聚合和标准布局是不同的东西。你可以有一个非标准的布局聚合。
  • @RemyLebeau 我想差不多,但不完全。一个聚合类可以有多个相同类型的(公共)基类子对象。标准布局类不能。当然,在std::array 的情况下,没有任何合理的实现会做这样的事情。
  • @RemyLebeau:我认为这是一个聚合,因为它是一个没有用户声明或继承的构造函数,没有私有或受保护的直接非静态数据成员等的类。但这没什么关于它的成员实际上是什么。我认为标准将允许class std::array&lt;T,N&gt; { public: int miscellaneous_junk; T actual_array[N]; };
  • @NateEldredge 该特定实现不符合要求。杂项垃圾成员会干扰聚合初始化。
猜你喜欢
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 2011-12-03
  • 2022-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
相关资源
最近更新 更多