Below is the simplified version of a class that I am trying to design
class Class {
public:
Class(std::unique_ptr<int>&& ptr): ptr_(std::move(ptr)) {}
private:
// works only if I change to std::shared_ptr or remove const qualifier
const std::unique_ptr<int> ptr_;
};
int main() {
std::unique_ptr<int> ptr = std::make_unique<int>(1);
Class c(std::move(ptr)); // Compiles
// I need to construct a new instance d, but c is not needed anymore,
// anything c owns can be nullified.
Class d(std::move(c)); // Does not compile
return 0;
}
I can not construct an instance d
from the instance c
due to the following error message:
Copy constructor of 'Class' is implicitly deleted because field 'ptr_' has a deleted copy constructor
However, if I change the ptr_
member to be of type const std::shared_ptr<int>
, then everything works.
Is there a way to have a single constructor for the Class
, which would support:
- Constructing the class instance directly providing the arguments it needs
- Constructing the class instance from another class instance, possibly destroying another instance in the process (i.e. c(std::move(d));
- Allowing the class to have a member of type
const unique_ptr
?
EDIT: constructing an instance d
using std::move(c)
also doesn't work:
class.cc:17:23: error: use of deleted function ‘Class::Class(Class&&)’ 17 | Class d(std::move(c)); | ^ class.cc:5:7: note: ‘Class::Class(Class&&)’ is implicitly deleted because the default definition would be ill-formed:
So far two solutions work for me, neither are perfect from readability point of view:
Remove
const
qualifier:std::unique_ptr<int> ptr_
;The problem is that this reads as: "ptr_ member might be modified when calling public methods"
Change to shared pointer:
const std::shared_ptr<int> ptr_
;The problem is that this reads as: "we have multiple pointers pointing to the same data"
Any way to improve the design which wouldn't have the problems outlined in (1) and (2)?
CodePudding user response:
- Constructing the class instance from another class instance, possibly destroying another instance in the process (i.e.
c(std::move(d)
);- Allowing the class to have a member of type
const unique_ptr
This intersection of desires is inherently contradictory.
Moving is a modifying operation. If the object is const
, you cannot move from it.
The entire purpose of unique_ptr
is that there is one instance in the program which uniquely owns (and will destroy) the object it points to. Therefore, it cannot be copied, because a copy is a non-modifying operation. If it could be copied, then two objects would try to own the same object.
You cannot copy from a unique_ptr
. You cannot move from a const
object of any kind. Therefore, if your object has a const unique_ptr
, it cannot get a pointer from some other instance of that object.
By declaring a member to be a const unique_ptr<T>
, you are also declaring that the object can be neither copied nor moved. Those are the consequences of your code choices.
So you're going to have to decide what is more important: the member being const
or the ability to copy/move from this object.
Broadly speaking, const
members create more problems than they usually solve. If the member is private, then that is usually good enough protection; users with direct access to the class should be able to know what each such function should and should not modify. And if they make a mistake, the code in which that mistake can appear is pretty limited.