46
votes

As per C++11 rules 6 things (default constructor, copy constructor, move constructor, copy assignment, move assignment and destructor ) are generated by default. By second rule, when any custom copy, move or destructor is defined then those default operations are not generated. But in my code that follows that is not the case. But this code fails to compile with an error

call to implicitly deleted copy constructor of 'Uni'

When I write my own copy constructor for Uni everything works fine. (It is commented in the code, given for reference )

Any thoughts much appreciated.

Finally , I am running this on Mac, Xcode with LLVM compiler.

many thanks...

#include <iostream>

class A
{
public:
    A(int i) :num{i}
    {
        std::clog<< "ctor  A() num = " << num << "\n";

    }
    A( A const &aRef)
    :num{aRef.num}
    {
        std::clog << " copy ctor A( A const &aRef) num = " << num << "\n";
    }

    int value()
    {
        return num;
    }

private:
    int num;

};
class Uni
{

public:
    Uni(A* aptr) : up{aptr}
    {
        std::clog << " ctor Uni value = " << up.get()->value() << "\n";
    }
    /*Uni(Uni const &uRef)
    {
        std::clog << " copy ctor Uni copying obj pointed by unique_ptr\n";
        up.reset(uRef.up.get() ? new A{*uRef.up.get()} : nullptr);
    }*/
private:
    std::unique_ptr<A> up;

};

int main(int argc, const char * argv[])
{
    Uni one{new A{10}};
    Uni two{one}; //default copy ctor is implicitly deleted. why ?
}
1
unique_ptr cannot be copied, which causes Uni's copy constructor to be defined as deleted, which causes the compile error. When you write your own you obviously don't get this problem.Simple

1 Answers

49
votes

C++11 rules for automatic generation of special members aren't as simple as you posted them. The most important distinction is that in some cases, the member is implicitly declared, but defined as deleted. That's what happens in your case.

C++11, [class.copy]§11:

A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:

  • a variant member with a non-trivial corresponding constructor and X is a union-like class,
  • a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
  • a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
  • any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
  • for the copy constructor, a non-static data member of rvalue reference type, or
  • for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.

(Emphasis mine)


More generally, the rules for auto-generated class members are:

  • If the class has no user-provided constructors, a default constructor is declared.

  • If the class doesn't have a user-provided copy constructor, one is declared.

  • If the class has none of { user-provided copy or move constructor, user-provided copy or move assignment operator, user-provided destructor }, a move constructor will be declared (but see (*) below).

  • If the class doesn't have a user-provided copy assignment operator, one is declared.

  • If the class has none of { user-provided copy or move constructor, user-provided copy or move assignment operator, user-provided destructor }, a move assignment operator will be declared (but see (*) below).

  • If the class doesn't have a user-provided destructor, one is declared.

Any automatically declared member can be defined as defaulted (doing the default stuff) or defined as deleted (if you try to use it, you get an error). The rule of thumb is "If the defaulted version makes sense, it will be defined as defaulted. Otherwise, it will be defined as deleted."

In this context, "makes sense" means "doesn't try to call a deleted, ambiguous, inaccessible or otherwise illegal function." For example, the standard bit I quoted in the first part of this answer lists what doesn't "make sense" for copy constructors.

Additionally, an automatically declared copy constructor or copy assignment operator is defined as deleted if the class has a user-provided move constructor or move assignment operator.

(*) If an automatically declared move constructor or move assignment operator would be defined as deleted, it is instead not declared at all. This rule exists so that trying to move such a class implicitly falls back to copying it instead of generating an error.