4

我想知道为什么这个程序不能编译(在 msvc、gcc 和 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();
}

按照我的预期,删除的拷贝构造函数应该让其他ActionDecorator实例来构造ActionDecorator,因为它是Action的多态类型。相反,我必须将 ActionDecorator 实例显式转换为 Action&,因为编译器抱怨试图引用已删除的复制构造函数。是否有一些标准规则可以解释这种行为?

4

1 回答 1

6

删除 aa 函数不会将其从重载决议中删除。该功能仅定义为已删除。目的是在重载决议选择该函数时使程序格式错误。

由于复制 c'tor 比您提供的特殊基类 c'tor 更匹配,因此如果您不强制转换,重载决议将始终选择它。

如何最好地处理它是有争议的。从理论上讲,您可以让复制 c'tor 进行类似的包装。然而,我对有一个不复制的复制 c'tor 感到很痛苦。你的体重可能非常。

另一种我个人更喜欢的选择是不按原样提供公共构造函数。相反,让客户通过常规命名函数创建装饰器。像这样的东西:

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

现在该类可以真正保持不可复制,并且客户将永远不需要自己转换它。如果他们传递一个ActionDecoratorto decorate,它将Action在构造实例之前绑定到一个引用。所以它甚至不会考虑copy c'tor。

但是,该类必须是可移动的,这样才能在 C++17 之前工作。

于 2018-03-08T12:59:57.563 回答