问题
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