Christian Rau
Christian Rau

Reputation: 45948

trivial and POD types with user-defined constructors

First of all, this is more of a sanity check question to get some approval by people better-versed in the depths of the language standard than me.

Let's say I have the following types (though I left out any non-constructor and non-assignment member functions):

template<typename E> struct half_expr
{
};

class half : public half_expr<half>
{
public:
    half();
    explicit half(float);
    template<typename E> half(const half_expr<E>&);

    half& operator=(float);
    template<typename E> half& operator=(const half_expr<E>&);

private:
    half(std::uint16_t, bool);

    std::uint16_t data_;
};

Well, on any reasonable implementation a half shouldn't be anything else in memory than a plain std::uint16_t. But I'm interrested in the guarantees from the standard. Here is my rationale according to the C++98/03 defition of POD:

and the C++11 losened/extended definitions:

My quite simple question is just: Is my rationale entirely correct or are there any things I overlooked or misinterpreted in the definitions, especially in light of the user-defined constructors and assignments somehow hindering a classification as trivially copyable?

Note: Even if you feel the urge to delegate this to some possible duplicate about POD and standard-layout types (which I could prefectly understand), a simple comment answering my actual question would still be nice, since this is a mere sanity check, which might occur simple or superflous to you, but I just want to be on the safe side.

Upvotes: 2

Views: 1193

Answers (1)

Steve Jessop
Steve Jessop

Reputation: 279335

Yes, half is standard-layout (9/7): the base class is empty, the derived class has the same access control for all non-static data, there's nothing virtual and no non-standard-layout bases or members, and the base is a different type from the first non-static data member.

The constructors and assignment operators that you define are not copy (or move) constructors and assignments, so they have no bearing on whether the class is trivially copyable.

The default constructor is non-trivial because it is user-provided (12.1/5), and so the class is not trivial.

The copies/moves are all trivial because the data member and the empty base have trivial copies/moves (12.8/12). Same for the destructor, and so the class is trivially copyable.

So I believe your analysis is correct - remove the no-arg constructor and you have a POD class in C++11 (but not in C++98).

As Luc says, there is no guarantee in the standard that a POD class contains no padding, even if it has only one data member.

In C++03, the empty base class optimization is permitted, not required, so a poor-quality but conforming C++03 implementation could certainly give you sizeof(half) == sizeof(half_expr<half>) + sizeof(uint16_t). It is guaranteed that sizeof(half_expr<half>) > 0, and if it's smaller than uint16_t you could reasonably expect padding too.

In C++11, the rules on layout-compatibility in effect require that the empty base class optimization is applied -- two standard-layout types in which one has a base class and the other does not, are layout compatible (9.2/17), and layout-compatible types can be read through a union (9.2/19) which means they must have the same layout. Padding is still permitted, but pointless.

The standard aside, your implementation may or may not be working from a C++ ABI that tells you what POD classes look like. If so, I would expect that although the ABI might not be up-to-date for C++11 yet, surely the implementation will make all standard-layout classes look the way POD classes are defined to look, regardless of whether they are also trivial (and it will do the EBC optimization). That's kind of the point of standard-layout: "classes laid out like C structs".

I think that implementations are also permitted to arbitrarily decide that all class types must be 4-aligned, in which case again you'd get padding. I don't think it's reasonable to expect this, though, since the only motivation I can think of for doing it is that you're on some bizarre architecture where 4-aligned pointers can be smaller than char*.

Upvotes: 6

Related Questions