Search code examples
c++polymorphismcopy-constructordeleted-functions

Why deleted copy constructor doesn't let to use other constructor with polymorphic type?


I wonder why this program doesn't compile (the same behavior on msvc, gcc and clang):

#include <iostream>

using namespace std;

struct Action
{
    virtual void action()
    {
        cout << "Action::action()\n";
    }
};

struct ActionDecorator : Action
{
    ActionDecorator(const ActionDecorator&) = delete;
    ActionDecorator(Action & action) : origAction(action)
    {
    }

    void action() override
    {
        decoration();
        origAction.action();
    }

private:
    void decoration()
    {
        cout << "ActionDecorator::decoration()\n";
    }

    Action & origAction;
};

int main()
{
    Action action;
    ActionDecorator actionDecorator(action);
    ActionDecorator actionDecorator2(actionDecorator);
    actionDecorator2.action();
}

According to my expectation, deleted copy constructor should let construct ActionDecorator by other ActionDecorator instance, as it is polymorphic type of Action. Instead I have to explicit cast ActionDecorator instance to Action& as compiler complains about attempting to reference a deleted copy constructor. Is there some standard rule which explains such behavior?


Solution

  • Deleting a a function doesn't remove it from overload resolution. The function is merely defined as deleted. The purpose is to make the program ill-formed when overload resolution chooses that function.

    Since the copy c'tor is a better match than the special base class c'tor you provided, overload resolution will always pick it if you don't cast.

    How to best handle it is debatable. You could theoretically have the copy c'tor do a similar sort of wrapping. I'm torn about having a copy c'tor that doesn't copy, however. Your millage may very.

    Another option, which I'm personally much more comfortable with, is to not provide public constructors as is. Instead, have clients create decorators via a regular named function. Something like this:

    ActionDecorator decorate(Action& action) {
      return {action};
    }
    

    Now the class can truly remain non-copyable, and clients will never need to cast it themselves. If they pass an ActionDecorator to decorate, it will bind to an Action reference prior to constructing the instance. So it won't even consider the copy c'tor.

    The class will have to be movable, for this to work prior to C++17, however.