Why is a struct with a deleted copy constructor not a POD type?
Asked Answered
W

2

9

We have the following method to test if our structure is a POD or not. It always returns true:

bool podTest() {
  struct podTest {
    int count;
    int x[];
  };

  return std::is_pod<podTest>::value;  //Always returns true
}

So far so good. Now we make one change and delete the copy constructor:

bool podTest() {
  struct podTest {
    podTest(const podTest&) = delete;
    int count;
    int x[];
  };

  return std::is_pod<podTest>::value;  //Always returns false
}

This always returns false. After reading over the definition of is_pod I am still struggling to understand what requirement it violates. What am I missing?

This is being compiled on godbolt using gcc 6.1, with -std=c++14

Whitewood answered 6/2, 2017 at 23:1 Comment(4)
At a guess because you are saying the struct cannot be copied, but one of the qualities of POD types is that they are always copyable.Altruistic
@NeilButterworth: One of the requirements of POD types is, that they are trivially copyable.Topknot
Oddly enough, std::is_trivially_copyable<podTest>::value returns true, so there must be some other violation of POD requirements. Certainly int x[]; should be removed. Unless I'm mistaken, flexible array bounds aren't part of C++14.Topknot
@Topknot libstdc++ implements std::is_pod with a compiler hook it seems, not via the std::is_trivially_copyable. libc++ also uses the compiler hook if available, but its fallback implementation is wrong too because Trivially Copyable is not the same as (Trivially Copy Constructible && Trivially Copy Assignable), which is what they check for.Hibernal
A
9

Aha!

From [class]:

A POD struct is a non-union class that is both a trivial class and a standard-layout class

where a trivial class is:

A trivial class is a class that is trivially copyable and has one or more default constructors (12.1), all of which are either trivial or deleted and at least one of which is not deleted.

But in [class.copy]:

If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted (8.4).

Your podTest, when you explicitly deleted the copy constructor, has no default constructor. So it's not a trivial class, so it's not a POD. If you add in:

podTest() = default;

Then it'll become a POD again.

Apeman answered 6/2, 2017 at 23:38 Comment(5)
So in this case, a class without a copy constructor can still be copied with memcpy()!Cinerary
If there are two default ctors, with one deleted and one not. Is there not an ambiguity if you try to create an object of the class? I wonder about the reason for this "at least one non-deleted" rule.Tatianatatianas
@JohannesSchaub-litb It's possible to be non-ambiguous: struct X { X() = default; template <class... Args> X(Args&&...) = delete; }; X x;Apeman
@Apeman correct, but the text makes even the ambiguous cases PODs. And if you placed the = delete onto the non-template and didn't delete the other template default-ctor, it would still have marked it as a POD.Tatianatatianas
@JohannesSchaub-litb Yeah I don't know. It seems like a very specific wording choice.Apeman
A
2

Because deleted copy constructors are allowed for POD types only after C++14. I would assume, you are compiling your code in C++11 mode.

Ashla answered 6/2, 2017 at 23:6 Comment(11)
The struct above is trivially copyable in both C++11 and C++14, and according to cppr, that's all the copy semantics we need for POD.Hibernal
It's so strange to me that a noncopyable type can be POD. I thought POD types were supposed to have the same behaviour in C and C++.Haveman
@BaummitAugen, is this page: en.cppreference.com/w/cpp/concept/TriviallyCopyable wrong than?Ashla
@Ashla Probably. #29759941 Edit: No, I don't think so. Looks a little inprecise at most.Hibernal
@BaummitAugen, i think, this answer is outdated..?Ashla
@Ashla You mean in the question I linked? That handles both C++11 and C++14, so I don't think so.Hibernal
I've edited the question to specify the compiler/language version since it appears to be relevant.Whitewood
Actually, I cannot find the update from the CWG fix T.C. cites in N4141; the rules there seem to match C++11. So either there is a major difference between N4141 or the entire C++14 part on Trivial Types, including trivially copyable, is just wrong.Hibernal
Had to dv, Barry found the correct reason, proving this answer wrong.Hibernal
@Brian struct X { X(X const&) = delete; } is trivially copyable. I love C++. Then again, you can't really have a noncopyable type in C right?Apeman
@Apeman And indeed, you are allowed to memcpy struct X, as it is trivially copyable, even when the copy constructor is deleted. It doesn't feel right, though.Cinerary

© 2022 - 2024 — McMap. All rights reserved.