【问题标题】:Call to implicitly deleted copy constructor调用隐式删除的复制构造函数
【发布时间】:2017-07-29 04:56:35
【问题描述】:

我有以下设置:

class MyClass {
 public:
  static MyClass Clone(const MyClass& other) { 
    return MyClass(other, 10);
  }

  static MyClass CreateNormal(int x, int y) {
    return MyClass(int x, int y);
  }

  // There are a few more factory functions.

 private:
  // Constructor 1
  MyClass(int x, int y) : b_(x, y) {}

  // Constructor 2
  MyClass(const MyClass& other, int c) : b_(other.b_, c) {}

  // And a lot more constructors.


  // NotMyClass has its copy constructor deleted.
  NotMyClass b_;
}

int main() {
  MyClass A(1,2);

  MyClass B = MyClass::Clone(A); // ERROR
}

有没有办法在不修改NotMyClass 的情况下解决这个问题? 发生错误是因为 MyClass 的复制构造函数被隐式删除。有没有办法可以使用 std::Move() 来解决这个问题?

我不能删除工厂函数,因为实际的类有很多构造函数,需要工厂函数来清晰和理解。

【问题讨论】:

  • 您可以使用哪种 c++ 标准?
  • NotMyClass 有移动构造函数吗?
  • 如果你想克隆一个不可复制的对象那么你最好希望NotMyClass本身有一个clone()方法。否则你试图打破它的设计。您确定要复制这个不可复制的东西吗?你不想引用它吗?
  • 如果NotMyClass 不提供复制构造函数,那几乎肯定是有原因的。我认为它是库的一部分,因此请阅读有关它的文档,了解为什么它不能被复制以及您有哪些替代方案。如果它是不应该被复制的东西,那么您可能只需要创建一个然后有一个NotMyClass*NotMyClass&。否则,它们可能会提供工厂函数,您可以在复制构造函数的初始化列表中调用它们。
  • 是的。 NotMyClass 没有复制构造函数。但它确实提供了重复的功能。能够为使用重复功能的 MyClass 创建我自己的 CopyConstructor。谢谢!

标签: c++ std copy-constructor factory-pattern move-constructor


【解决方案1】:

假设 c++11 标准的可用性,您可以延长工厂方法返回的右值引用的生命周期,而不是构造一个新对象,如下所示:

MyClass   A(1, 2);
MyClass&& B = MyClass::Clone(A);

附录: 正如 patatahooligan 在下面的评论中所指出的,您的 MyClass::Clone 版本本身可能存在复制构造函数调用。这可以/需要重写为

MyClass MyClass::Clone(const MyClass& other) {
    return {other, 10};
}

一旦 C++17 出现强制复制省略,所有这些都将完全没有必要,所以你可以期待。


在 C++03 中,尽管解决方案更长并且需要更多代码,但仍然可以实现预期的结果。我提供了两种选择:

我建议不要创建一组静态方法,而是使用标签调度来表示不同的构造函数含义。这是通过使用具有明确名称但不包含数据的类型参数重载构造函数来实现的。

struct clone_tag {};
class MyClass {
public:

    MyClass(int x, int y) : b_(x, y) {}
    MyClass(const MyClass& self, clone_tag) : b_(self.b_, 10) {}
private:

    NotMyClass b_;
};

int main() {
    MyClass   A(1, 2);
    MyClass   B(A, clone_tag());
}

或者这甚至可以达到真正的工厂模式,其中构建工厂和对象的最终构建是分开的。这显然需要更多时间来设置,但可以提高代码质量,具体取决于不同构造函数路径的实际数量。

class MyClass {
public:
    struct Clone {
        Clone(const MyClass& self) : self(self) {}
        const MyClass& self;
    };
    struct Init {
        Init(int x, int y) : x(x), y(y) {}
        int x, y;
    };

    MyClass(Init i) : b_(i.x, i.y) {}
    MyClass(Clone c) : b_(c.self.b_ , 10) {}
private:
    MyClass(const MyClass& other, int c) : b_(other.b_, c) {}

    NotMyClass b_;
};


int main() {
    MyClass   A = MyClass::Init(1, 2);
    MyClass   B = MyClass::Clone(A);
}

再想一想,我意识到给出三个不同的选择可能会比一个不太精确的简短答案造成更多的混乱。因此,我尝试按所需重构工作的顺序列出它们,尽管这只是对您的真实代码库的猜测。

【讨论】:

  • 好主意,但我相信临时本身不能在克隆的返回语句中构造,因为它调用了 MyClass 的私有复制构造函数,它将无法复制 MyClass::b_,因为它是非复制的-可构造NotMyClass
  • @patatahooligan 你指的是我的任何代码吗?因为我假设 OP 的所有代码都可以编译,除非特别提到。
  • 我的错,我误读了代码并认为b_ 被复制了,但实际上它被构造为b_(other.b_, c)
  • @patatahooligan 你是正确的MyClass::Clone 虽然在原始代码中,我只是将它添加到答案中。
猜你喜欢
  • 1970-01-01
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
相关资源
最近更新 更多