c++constructorimplicit

why std::make_unique<A>(*this) could call an implicitly-declared copy-constructor of A


The source question is from Usage of this* in make_unique

The code is below, and the best answer says:

In clone(), *this is an lvalue-reference to A, so you are constructing a A from an (lvalue-reference to) A (inside std::make_unique), so you are using the implicitly declared copy-constructor of A:

A(A const&);

I am confused that class A has a virtual destructor virtual ~A(){}, the compiler will not generate a copy constructor any more. So why std::make_unique<A>(*this) could call a implicitly declared copy-constructor of A?

class Base {
    public:
        virtual ~Base() {}
        virtual std::unique_ptr<Base> clone() = 0;
        virtual void print() = 0;
};

class A: public Base {
        std::string name_;
    public:
        A(std::string name ){name_ = name;};
        std::unique_ptr<Base> clone() override{
            return std::make_unique<A>(*this);
        };
        void print( ) override{
            std::cout << "Class A: " << name_;    
        };
        virtual ~A(){};
};

class Factory{
        std::unique_ptr<A> type = std::make_unique<A>("MyName");  
    public:
        std::unique_ptr<Base> createInstance(){
            return type->clone();
    }
};

int main(){
    Factory factory;
    auto instance = factory.createInstance();
    instance->print();
}

We all know the rule that a C++ class defines destructor function, the compiler will not generate default copy constructor and overloaded assignment operator, and the moving version for the two, look at the definition in class A, it defines virtual ~A(){}, so there will not a implicit copy constructor to be called for std::make_unique<A>(*this).


Solution

  • We all know the rule that a c++ class defines destructor function, the compiler will not generate default copy constructor and overloaded assignment operator,

    That's not correct. The compiler declares and defines the implicit copy operations regardless of the user-declared destructor.

    However this behavior has been deprecated since C++11. Still, it is not removed from the language (and I do not expect it to be removed any time soon). So at best you can expect the compiler to warn you about the deprecated behavior (for example Clang does with -Wdeprecated), but it still has to compile the program with the implicit copy operations, for now. (Of course you could make e.g. the -Wdeprecated warning an error with -Werror=deprecated in Clang and other compilers may have similar options.)

    That's also why following the rule-of-0/3/5 is so important. The compiler will still generate the copy operations implicitly even if they are likely to not behave correctly for a type with user-declared destructor. So you should either define the copy (and move) operations manually or define them as deleted (if copying should be impossible) when you have a user-declared destructor.