将C风格的数组视为std :: array

32
有没有一种安全并符合标准的方法,可以将C风格数组视为std :: array,而不必将数据复制到新的std :: array中?
这显然无法编译,但是这是我想要的效果(我的实际用途更为复杂,但是这个简短的示例应该展示了我想做的事情)。我猜reinterpret_cast可能“有效”,但可能不安全?
#include <array>

int main()
{
    int data[] = {1, 2, 3, 4, 5};

    // This next line is the important one, treating an existing array as a std::array
    std::array<int, 5>& a = data;
}

感觉应该是可以的,因为数据应该是相同存储的。

编辑:清楚地说,我不想清除一个新的std :: array,我想将现有数据引用为一个。


1
STL容器管理它们自己的内存。您不能创建一个数组并让它管理在其他地方分配的某个数组。 - krammer
2
考虑到std::arraystd::vector期望管理它们自己的内存,您在使用reinterpret_cast时应非常小心,而不采取措施确保它们不会尝试删除不受其控制的数据。但是除此之外...不要害怕memcpy。毕竟,它是一个相当高效的例程。 - Rook
好的,谢谢。我想要安全地完成这个任务,而不是采用黑客手段。我只是想知道是否有可能实现 :) - jcoder
3
std::array 是一种聚合类型,官方上并不算是容器。它没有动态分配的内存,因此内存管理非常简单。 - juanchopanza
我同意@Juanchopanza的观点,我甚至可以说它们可以完全相同。对于krammer和Rook:std::array与std::vector非常不同,std::array不会重新分配内存,其大小是硬编码的。 - Michael
3个回答

27

如在此帖子中所讨论的那样:

std::array<int, N>是POD,因此标准布局。据我理解标准布局的要求,这意味着对象的指针与第一个成员的指针相同。由于根据http://en.cppreference.com/w/cpp/container/array,std::array没有私有或保护成员,因此这应该与包装数组中的第一个元素一致。因此,类似这样的代码:

reinterpret_cast<std::array<int, 5>*>(&data)

在我看来是符合标准的。不过,我得承认,有时我也很难解释标准语言,如果我错了,请纠正我。

祝好

Claas


3
我认为你是正确的,但我会检查一下。array5 * p = reinterpret_cast<array5*>(&data); ASSERT(p->data()==&data); 这正是我们最终想要的。 - CoffeDeveloper

14

您可以使用 reinterpret_cast,但请注意,这是一个丑陋的肮脏技巧,在您的实际发布代码中不应该这样做:

std::array<int, 5> &a = reinterpret_cast<std::array<int, 5>&>(data);

如果 std::array 的内部实现发生变化(例如,在调试版本的STL中添加了一些附加字段以进行一些运行时检查),则可能会出现问题。然后,此代码将开始崩溃,而没有任何有用的信息(因为它基于 std::array 对象和 C 数组具有相同的内存布局的隐含假设)。

如果您决定不管三七二十一使用丑陋的恶心的hack,至少要添加一个编译时大小检查:

    C_ASSERT(sizeof(a) == sizeof(data));

如果 std::array<> 的大小不再匹配您的 C 数组的大小(由于 STL 实现中的某些更改),则会产生错误。


是的,我让它“工作”了。我只是想知道是否有一种不那么丑陋肮脏的方法 :) 谢谢 - jcoder
4
reinterpret_cast这个名称是故意使用丑陋的词汇,以明确表示正在使用一种丑陋的技巧。 - MSalters
如果您告诉我们使用std::array的目的是什么(使用迭代器?算法?),我们可能会建议一种非hack的方法。 - Ivan Shcherbakov
3
C++规定,类型std::complex与 C语言的_Complex类型具有布局兼容性,例如:“表达式 reinterpret_cast<cv T(&)[2]>(z) 必须是良构的,reinterpret_cast<cv T(&)[2]>(z)[0] 应指定z的实部”,等等。因此,这种情况并非没有先例。std::array不要求与原始数组具有布局兼容性(例如,std::array可能具有额外的成员,在这种情况下,您想要的实际上是不可能的),但如果它确实具有布局兼容性,则我认为此代码是合法的,尽管难看,不算是一种肮脏的技巧。 - bames53
5
@bames53 在使用 reinterpret_cast 时,最好加上类似于 static_assert(sizeof(data) == sizeof(std::array<int, 5>), "std::array<T, N> 和 T[N] 的布局不兼容。"); 这样的前置条件。这将提供清晰的错误消息,指示问题,并有效地未来证明(以及防止奇怪的编译器问题)强制类型转换。 - Justin Time - Reinstate Monica
显示剩余4条评论

12

您无法这样做。 std::array 是一个聚合类型,它持有自己的数据块(而不是指向可轻松重新分配的数据块的指针)。因此,无法避免对所有元素进行复制。在C++11中,这尤为重要,因为数组的数据不能移动,所以没有高效的 std::swap 函数。


7
我理解了。我不想创建一个带有自己数据的新std::array,我想引用现有数据,就像它是一个std::array一样,因为它们可能具有相同的布局。如果可以安全地执行,那就最好了。我相信使用reinterpret_cast "hack"应该可以“工作”... - jcoder
无论如何,我还是点了赞。即使“抱歉,你不能安全地这样做”不是我所希望的答案,但它确实是对问题的一个好回答。 - jcoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接