0

I have the following sample:

#include <vector> class noncopyable { protected: noncopyable() {} ~noncopyable() {} noncopyable(const noncopyable&) = delete; noncopyable& operator=(const noncopyable&) = delete; noncopyable(noncopyable&&) = default; noncopyable& operator=(noncopyable&&) = default; }; class C1 : private noncopyable { public: C1() { } ~C1() { } }; int main() { std::vector<C1> v; v.emplace_back(); return 0; } 

I thought it should work since C1 should be movable since it's base class is and it has no data members. Instead, I got an an error (using clang++):

error: call to implicitly-deleted copy constructor of 'C1' { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); } ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ . . . note: in instantiation of function template specialization 'std::vector<C1, std::allocator<C1> >::emplace_back<>' requested here v.emplace_back(); ^ note: copy constructor of 'C1' is implicitly deleted because base class 'noncopyable' has a deleted copy constructor class C1 : private noncopyable { ^ note: 'noncopyable' has been explicitly marked deleted here noncopyable(const noncopyable&) = delete; 

Doing a little research (http://en.cppreference.com/w/cpp/language/move_constructor) revealed that if there is a user-defined destructor then no implicit move-constructor will be defined. This seemes to be the problem here, since C1 has a destructor, the move-constructor does not get defined. Sure enough, if I either remove the destructor or add C1(C1&&) = default; to C1 then it works.

So far so good.

The problem was that the error message didn't mention ~C1() or the move-constructor. It said it was trying to call the copy constructor, which was deleted in the base class. So I tried changing the deleteed functions in noncopyable to be defaulted instead, and (surprise!), that also solved the error.

So my question is, what does this last thing have to do with the error or it's correction? If there is a destructor, what is the difference if the base class has the copy-constructor or not?

2
  • 1
    Try adding noexcept specifier. Commented Feb 1, 2017 at 22:58
  • Note there's a difference between "does not have a move constructor" and "has a deleted move constructor". Commented Feb 1, 2017 at 23:07

1 Answer 1

4

You don't need vector, a simpler example would be just:

C1 a; C1 b(std::move(a)); // error: C1's copy constructor is deleted 

From [class.copy]:

If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
(9.1) — X does not have a user-declared copy constructor,
(9.2) — X does not have a user-declared copy assignment operator,
(9.3) — X does not have a user-declared move assignment operator, and
(9.4) — X does not have a user-declared destructor.

C1 has a user-declared destructor, therefore it does not have a move constructor. C1 does however have an implicitly declared copy constructor

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

The full set of constructors on C1, explicit and implicit, looks like:

C1(); C1(C1 const& ) = default; // but also delete ~C1(); 

So trying to construct a C1 from an rvalue of type C1 will match that implicitly declared copy constructor as the best match (nothing else is viable), but that constructor is deleted because noncopyable's copy constructor is deleted, so the entire expression is ill-formed.

That's why the error message mentions the constructor. That move-construction is ill-formed because the best match for that move-construction is the copy constructor is ill-formed. It can't mention the move constructor because there is no move constructor, and the destructor isn't relevant to the expression at hand. When you changed the base class to be copyable, now C1 also becomes copyable - so no error. There's still no move constructor, it's just that now there's a viable candidate for move construction.

Sign up to request clarification or add additional context in comments.

2 Comments

How can a constructor be both default and delete? Also, can you please explain the last sentence in your second quote? What does it mean the the case is "deprecated"? Doesn't this mean that if I have a user-defined destructor I wont get the implicit copy-constructor (same as for the move-constructor)?
@baruch It's defaulted. But the default is ill-formed, so it's deleted. Deprecated just means that at some indeterminate point in the future, the functionality will change. Don't worry about it. I just included it for completeness.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.