std :: array位是否与旧的C数组兼容?

shi*_*jin 33 c++ memcpy reinterpret-cast c++11

是底层位表示一个std::array<T,N> vT u[N]一样的吗?

换句话说,将N*sizeof(T)字节从一个复制到另一个是否安全?(通过reinterpret_castmemcpy.)

编辑:

为了澄清,重点是相同的位表示reinterpret_cast.

例如,假设我将这两个类放在一些简单的可复制类型上T,对于某些类N:

struct VecNew {
    std::array<T,N> v;
};

struct VecOld {
    T v[N];
};
Run Code Online (Sandbox Code Playgroud)

还有遗留功能

T foo(const VecOld& x);
Run Code Online (Sandbox Code Playgroud)

如果表示相同,则此调用是安全的并且避免复制:

VecNew x;
foo(reinterpret_cast<const VecOld&>(x));
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 20

这并不直接回答您的问题,但您应该只使用std::copy:

T c[N];
std::array<T, N> cpp;

// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));

// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));
Run Code Online (Sandbox Code Playgroud)

如果T是一个简单的可复制类型,这将编译为memcpy.如果不是,那么这将做元素方面的复制分配并且是正确的.无论哪种方式,这都是正确的事情并且非常易读.无需手动字节算术.

  • 我被撕裂了.对于另一个问题,这是一个很好的答案! (8认同)
  • nitpick:`std :: copy`并不总是编译成`memcpy`这是一个实现细节.例如,VC++使用`memmove`作为字节副本. (7认同)

Sla*_*ica 13

std::array提供方法data(),可用于复制到适当大小的c风格数组:

const size_t size = 123;
int carray[size];
std::array<int,size> array;

memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );
Run Code Online (Sandbox Code Playgroud)

文档中所述

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

所以内存占用似乎与c风格的数组兼容,但是reinterpret_cast当你有一个没有任何开销的正确方法时,你不清楚为什么要使用"hacks" .

  • 这正是我想要澄清的"看似"的部分. (3认同)

eca*_*mur 9

data()方法的要求是它返回一个指针T*,使得:

[data(), data() + size())是一个有效的范围,和data() == addressof(front()).

这意味着您可以通过data()指针顺序访问每个元素,因此,如果T可以轻松地复制,您确实可以使用从数组memcpy复制sizeof(T) * size()字节T[size()],因为这相当于memcpy单独使用每个元素.

但是,你不能使用reinterpret_cast,因为这会违反严格的别名,因为data()不需要实际上由数组支持 - 而且,即使你要保证std::array包含一个数组,因为C++ 17你不能(甚至使用reinterpret_cast)将指向数组的指针转换为指向其第一个成员的指针(您必须使用std::launder).

  • Re"自C++ 17以来你不能(甚至使用reinterpret_cast)向指向第一个成员的指针(从而必须使用std :: launder)投射指向数组的指针",这听起来很有趣:委员会疯狂地狂暴!更多信息请.在此期间,我会做一些爆米花. (5认同)
  • @underscore_d不是关于危险,而是关于优化; 如果编译器假定不同大小的数组和指针不是别名,即使元素类型相同,也可以有效地加速许多科学代码(*cough*SPEC*cough*).这种产量的加速被认为(由编译器作者,并且公平地,他们的客户编写科学的,Fortran风格的代码)值得他们的用户编写更多系统或面向对象的代码的潜在混淆和破坏. (4认同)
  • 嗯,重新链接,这是一面文字.几百公里吧.你能模糊地指出一个不到203米的区域吗? (3认同)
  • @ Cheersandhth.-Alf"指向数组的指针无法转换为指向其第一个元素的指针":请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/ p0137r1.html (2认同)
  • 这似乎是为了让编译器供应商对标准的管理感兴趣,从而编译器的缺点和愚蠢行为变得标准化.那好吧. (2认同)
  • @ Cheersandhth.-Alf废话可能,但这是标准的方向:`int a; int(&r)[1] = reinterpret_cast <int(&)[1]>(a); r [0] = 42;`现在是UB.我们真的生活在有趣的时代. (2认同)
  • 你知道从事这项工作的人是否给出了改变的理由?喜欢他们希望实现的目标? (2认同)

rus*_*tyx 9

我说是(但标准并不能保证).

根据[array]/2:

数组是一个聚合([ dcl.init.aggr ]),可以使用最多N个元素进行列表初始化,这些元素的类型可以转换为T.

并[dcl.init.aggr]:

聚集体是一个数组或一个类(第[类])与

  • 没有用户提供的,显式的或继承的构造函数([class.ctor]),

  • 没有私有或受保护的非静态数据成员(Clause [class.access]),

  • 没有虚函数([class.virtual]),和

  • 没有虚拟,私有或受保护的基类([class.mi]).

鉴于此,只有在课程开头没有其他成员且没有vtable时,才能进行"可以列表初始化".

然后,data()指定为:

constexpr T* data() noexcept;

返回:指针,这[data(), data() + size())是一个有效的范围,和data() == addressof(front()).

该标准基本上想要说"它返回一个数组",但为其他实现留下了空间.

唯一可能的其他实现是具有单个元素的结构,在这种情况下,您可能会遇到别名问题.但在我看来,这种方法除了复杂性之外不会增加任何东西.将数组展开到结构中没有任何好处.

因此,作为数组实现是没有意义 的.std::array

但确实存在漏洞.


Jer*_*fin 7

array 对实例化它的基础类型没有太多要求.

要使用或执行复制有任何可能的结果,您实例化它的类型必须是可以轻易复制的.将这些项存储在一个中不会影响(和这样的)仅适用于普通可复制类型的要求.memcpyreinterpret_castarraymemcpy

array需要是一个连续的容器和一个聚合,这几乎意味着元素的存储必须是一个数组.标准显示为:

T elems[N]; // exposition only
Run Code Online (Sandbox Code Playgroud)

然而,它后来有一个注释,至少暗示它是一个数组是必需的(§[array.overview]/4):

[注意:成员变量elems仅用于展示,以强调这array是一个类聚合. 该名称elems不是数组接口的一部分. - 尾注]

[强调补充]

请注意它实际上只是elems不需要的特定名称.

  • [新草案](http://eel.is/c++draft/array)摆脱了那一部分.现在我们只知道它是一个可以用`N``T`s(但+1)初始化的列表. (2认同)