Is std::array<T, S> guaranteed to be POD if T is POD?
Asked Answered
H

3

22

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

Histochemistry answered 9/9, 2010 at 6:21 Comment(0)
O
14

§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.

Obala answered 9/9, 2010 at 8:2 Comment(4)
Bah, these slender formulations in the Standard... Why can't they write things out in the committee :(Confessedly
@Johannes: It seems to me there are more defects in the C++0x FCD than the C++98 FCD. What do you think will happen?Obala
What prevents the implementation from putting a, say, std::string in std::array<double, 10> after the built-in array member, though?Sassan
@Sassan I don't see why not. You might file a defect report by sending a short email to Daniel Krügler.Obala
S
8

By definition of POD:

9 Classes

9 A POD struct is a class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). A POD class is a class that is either a POD struct or a POD union.

[Emphasis mine]

std::array does satisfy all the requirements of being a trivial, standard-layout class template. So the answer to your question is yes.

Slander answered 9/9, 2010 at 6:58 Comment(1)
How do you guarantee that the implementation does not have a non-POD member? :-)Arjun
C
1

Potatoswatter found an error in my conclusions. C++ explicitly allows an implementation to explicitly define an assignment operator "with the same apparent semantics". This will make it a non-trivially copyable type. Making it community wiki...


It seems to me you don't want to test against PODnes, but against trivially copyable, which is way less restricting. Because that is how C++0x constraints types that can be used with memcpy and friends.

And while I don't think there are any guarantees about PODness of std::array, there are guarantees about trivial copyability, as the following shows (if I haven't got an error in the conclusions). As we know std::array is an aggregate, and aggregates are

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).

While trivially copyability is defined for a class that has class that

  • has no non-trivial copy constructors (12.8),
  • has no non-trivial move constructors (12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and
  • has a trivial destructor (12.4).

std::array has no destructor (as a comment in the definition of std::array says). This does not seem to follow from the definition of aggregate classes though, even though the comment in std::array's class definition claims that.

The remaining 4 requirements follow from the absence of bases, virtual functions and user provided versions for those 4 special member functions for aggregates.

Confessedly answered 9/9, 2010 at 6:22 Comment(2)
Hmm, doesn't N3126 §17.5.2.2/2 specifically allow std::array to have a non-trivial copy/move assignment operator? (Of course, that would be stupid, but it would still be an aggregate.)Obala
@Obala oh that seems to be the case. Interesting :)Confessedly

© 2022 - 2024 — McMap. All rights reserved.