是 std::array<T, S>如果 T 是 POD 就保证是 POD?

2021-12-13 00:00:00 templates c++ c++11 stl tr1

我目前正在编写 C++ 内存编辑库,对于读/写 API,我使用类型特征(std::is_pod、std::is_same)和 boost::enable_if 来提供 3 个重载:

  1. POD 类型.例如MyMem.Read(SomeAddress);
  2. 字符串类型.例如MyMem.Read>(SomeAddress);(这实际上不是读出 C++ 字符串,而是读出 C 样式字符串并将其转换为 C++ 字符串.)
  3. 矢量类型.例如MyMem.Read>(SomeAddress, NumElem);(这实际上并不是读出向量,而是读出 C 风格的数组并将其转换为向量.)

重载 2 和 3 只是重载 1 的包装器".(因此,如果您正在读取 std::vector 或 std::basic_string 并且 T 不是 POD,它将失败,因为它应该.)

最近我想使用 std::array 进行一堆读取和写入,因为我知道在编译时我想要读取和写入的数据的大小(我正在编写一个关于 PE 文件格式的包装器).

我编写了使用 std::array 的代码,然后打算添加另一个用于检测和处理 std::array 类型的重载,但我不小心点击了编译,令我惊讶的是它起作用了!

我目前正在使用 MSVC 10,结果证明对于 std::array,如果 T 是 POD,则 std::array 是 POD.(这意味着我可以只使用重载 1 并且它可以工作.)

我的问题是,这是由 C++ 标准保证还是由实现决定.

我知道我可以自己检查标准,但我并不像我信任本网站上的一些语言律师那样信任自己,所以我认为最好获得第二意见".;)

谢谢

附言此处提供的代码(它是仅标头库):http://code.google.com/p/hadesmem/source/browse/trunk/HadesMem-v2/Hades-Memory/Hades-Memory/MemoryMgr.h#86

解决方案

§23.3.1:

<块引用>

数组是一个集合(8.5.1),可以用语法array a初始化.= {初始化列表};其中 initializer-list 是一个逗号分隔的列表,最多包含 N 个元素,其类型可转换为 T.

在 C++03 中,POD 是根据聚合定义的:一个类,其中每个子对象都是原生的,或者聚合是 POD.因此,通过向后兼容性,C++0x std::array 是 POD.

或者,作为肛门,可以将 §9/5(定义平凡类)9/6(定义标准布局)和 9/9(将前面的要求组合到 POD)的要点与 8.5 的要点进行比较.1/1,定义聚合.

8.5.1:

<块引用>

聚合是一个数组或类(第 9 条),没有用户提供的构造函数(12.1),没有用于非静态数据成员的大括号或等号初始化器(9.2),没有私有或受保护的非静态数据成员(第 11 条),没有基类(第 10 条),也没有虚函数(10.3).

Clause 9 的要求确实涵盖了array,只要它的元素类型也是 POD 并且实现没有声明 operator=move> 里面 array 除了规范.

要真正肛门,17.5.2.2 说

<块引用>

  1. 为了说明起见,第 18 条到第 30 条和附录 D 没有描述复制/移动构造函数、赋值运算符或(非虚拟)析构函数,其语义与默认生成的语义相同(12.1,12.4、12.8).
  2. 未指定实现是否为此类成员函数签名或默认生成的虚拟析构函数提供了显式定义.

模板类数组的伪代码注释是

<块引用>

//没有显式构造/复制/销毁聚合类型

construct/copy/destroy 是否包括 operator=(赋值)或 move?它可能应该,但我认为,根据最严格的解读,它确实如此.

请注意,这不仅会影响"POD 性,还会影响"约翰内斯提到的微不足道的可复制性.

I'm currently writing a C++ memory editing library and for the read/write APIs I use type traits (std::is_pod, std::is_same) and boost::enable_if to provide 3 overloads:

  1. POD types. e.g. MyMem.Read(SomeAddress);
  2. String types. e.g. MyMem.Read>(SomeAddress); (This doesn't actually read out a C++ string, it reads out a C-style string and converts it to a C++ string.)
  3. Vector types. e.g. MyMem.Read>(SomeAddress, NumElem); (This doesn't actually read out a vector, rather it reads out a C-style array and converts it to a vector.)

Overloads 2 and 3 are simply 'wrappers' around overload 1. (So if you're reading a std::vector or std::basic_string and T is not POD it will fail, as it should.)

Recently I wanted to use std::array for a bunch of reads and writes because I knew the size of the data I wanted to read and write at compile time (I was writing a wrapper around the PE file format).

I wrote the code to use std::array, and then intended to add another overload for detection and handling of std::array types, but I accidentally hit compile and to my surprise it worked!

I'm currently using MSVC 10 and it turns out that for std::array if T is POD then std::array is POD. (Which means I can just use overload 1 and it works.)

My question is whether this is guaranteed by the C++ standard or left up to the implementation.

I know I could check the standard myself, but I don't trust myself as much as I trust some of the language lawyers on this site, so I figured it would be best to get a 'second opinion'. ;)

Thanks

P.S. Code available here (it's a header-only lib): http://code.google.com/p/hadesmem/source/browse/trunk/HadesMem-v2/Hades-Memory/Hades-Memory/MemoryMgr.h#86

解决方案

§23.3.1:

An array is an aggregate (8.5.1) that can be initialized with the syntax array a<T, N> = { initializer-list }; where initializer-list is a comma separated list of up to N elements whose types are convertible to T.

In C++03, POD was defined in terms of aggregate: a class where every subobject is native or an aggregate is POD. So, by backwards compatibility, a C++0x std::array is POD.

Or, to be anal, one can compare the bullet-points of §9/5 (defining trivial class) 9/6 (defining standard-layout) and 9/9 (combining preceding requirements into POD) with those of 8.5.1/1, which defines aggregates.

8.5.1:

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal- initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

Indeed the requirements in Clause 9 cover array as long as its element type is also POD and the implementation does not declare operator= or move inside array in addition to the specifications.

To be really anal, 17.5.2.2 says

  1. For the sake of exposition, Clauses 18 through 30 and Annex D do not describe copy/move constructors, assignment operators, or (non-virtual) destructors with the same apparent semantics as those that can be generated by default (12.1, 12.4, 12.8).
  2. It is unspecified whether the implementation provides explicit definitions for such member function signa- tures, or for virtual destructors that can be generated by default.

The note in the pseudo-code for template class array is

// No explicit construct/copy/destroy for aggregate type

Does construct/copy/destroy include operator= (assignment) or move? It probably should, but I don't think, by the strictest reading, it does.

Note that this "affects" not only POD-ness, but also trivial copyability as Johannes mentions.

相关文章