Why does C++ think my class is copy-constructible when it can’t be copy-constructed?
You said that you had a copy constructor, even though it can't be compiled. The post Why does C++ think my class is copy-constructible when it can’t be copy-constructed? appeared first on The Old New Thing.

Consider the following scenario:
templatestruct Base { // Default-constructible Base() = default; // Not copy-constructible Base(Base const &) = delete; }; template struct Derived : Base { Derived() = default; Derived(Derived const& d) : Base (d) {} }; // This assertion passes? static_assert( std::is_copy_constructible_v >);
Why does this assertion pass? It is plainly evident that you cannot copy a Derived
because doing so will try to copy the Base
, which is not copyable. Indeed, if you try to copy it, you get an error:
void example(Derived& d) { Derived d2(d); // msvc: error C2280: 'Base ::Base(const Base &)': // attempting to reference a deleted function // gcc: error: use of deleted function 'Base ::Base(const Base &) // [with T = int]' // clang: error: call to deleted constructor of 'Base ' }
Okay, so the compiler thinks that Derived
is copy-constructible, but then when we try to do it, we find out that it isn’t!
What’s going on is that the compiler is determining copy-constructibility by checking whether the class has a non-deleted copy constructor. And in the case of Derived
it does haev a non-deleted copy constructor. You declared it yourself!
Derived(Derived const& d) : Base(d) {}
So yes, there is a copy constructor. It can’t be instantiated, but the compiler doesn’t care. It is going based on what you tell it, and you told it that you can copy it.
After all, another possibly copy constructor would have been
Derived(Derived const& d) : Base() {}
and this one instantiates successfully. Copying a Derived
default-constructs the Base
base class rather than copy-constructing it.
Imagine that we moved the definition out of line.
templatestruct Derived : Base { Derived() = default; Derived(Derived const& d); };
What should the answer to the question “Is this copy-constructible?” be? You don’t know what the definition is, only its declaration. Should the compiler halt compilation with the error message “Unable to predict the future”? But what if you didn’t want the expose the implementation of the copy constructor in the header file?
The rule for determining copy constructibility is whether a non-deleted copy constructor is present. In the case of Derived
, it is present. It may not be instantiatable, but that’s not what is_
looks for.¹
Now, non-copyability inherits by default, so we could have just allowed the copy constructor to be defaulted:
templatestruct Derived : Base { Derived() = default; Derived(Derived const& d) = default; };
The implicitly-defined or explicitly-defaulted copy constructor is defined as deleted if any base class is not copy-constructible, in which case the declaration is treated as if it had said = delete
. That = delete
can be detected by is_
and result in the assertion failing.
But if you come out and make a custom copy constructor that is not deleted, the compiler assumes you will make good on your promise.
Related reading: Why does std::
report that a vector of move-only objects is copy constructible?
¹ Requiring that the type be complete and all members defined is not a reasonable requirement because that would require definitions of all class methods to be present in header files. Your entire program has been reduced to a header-only project.
The post Why does C++ think my class is copy-constructible when it can’t be copy-constructed? appeared first on The Old New Thing.