Call to implicitly deleted copy constructor

感情迁移 提交于 2021-02-11 12:13:04

问题


I have the following setup:

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
}

Is there a way to get around this without modifying NotMyClass? The error happens because the copy constructor of MyClass is implicitly deleted. Is there a way I can use std::Move() to solve this?

I cannot remove the factory functions as the actual class has many constructors and the factory functions are needed for clarity and understanding.


回答1:


Assuming the availability of the c++11 standard, you could extend the lifetime of the rvalue reference returned by the factory method instead of constructing a new object, like so:

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

Addendum: As noted by patatahooligan in a comment below, there might be a copy constructor invocation in your version of MyClass::Clone itself. This can/needs to be rewritten to

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

All of this will be completely unecessary once C++17 comes with mandatory copy-elision comes around, so you can look forward to that.


In C++03 it is still possible to achive the desired outcome albeit the solutions are longer and require more code. I offer two alternatives:

Instead of creating a set of static methods, I would advise for using tag-dispatch to signal different constructor meanings. This works by overloading the constructor with type arguments which have clear names but hold no data.

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

Or this could even go as far as a genuine factory pattern, where the building the factory and final construction of the object is seperated. This clearly needs more time to set up but could improve code quality, depending on the actual number of different constructor paths.

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

On second thought I realize that giving three different alternatives could cause more confusion than a short answer which is less precise. I therefore tried to list them in order of needed refactoring work although this is only guess work about your real code base.



来源:https://stackoverflow.com/questions/45385437/call-to-implicitly-deleted-copy-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!