调用隐式删除的复制构造函数

时间:2017-07-29 04:56:35

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

我有以下设置:

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()来解决这个问题?

我无法删除工厂函数,因为实际的类具有许多构造函数,并且为了清晰和理解需要工厂函数。

1 个答案:

答案 0 :(得分:2)

假设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中,尽管解决方案更长并且需要更多代码,但仍然可以实现期望的结果。我提供两种选择:

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

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);
}

第二个想法我意识到,提供三种不同的选择可能会导致更多的混乱而不是一个不太精确的简短回答。因此,我尝试按照所需的重构工作列出它们,尽管这只是猜测你真正的代码库。