Consider the following simplified example of a class holding a shared resource:
class array { typedef std::array<float, 1000> resource; public: // default constructor, creates resource array() : p_(std::make_shared<resource>()), a_(0), b_(1000) {} // create view array operator()(int a, int b) { return array(p_, a_+a, a_+b); } // get element float& operator[](int i) { return (*p_)[a_+i]; } private: std::shared_ptr<resource> p_; int a_, b_; // constructor for views array(std::shared_ptr<resource> p, int a, int b) : p_(p), a_(a), b_(b) {} }; Now I'm wondering how to define a semantics for this class that doesn't confuse its users. For example, I'd like to allow operator=() to copy elements:
array x, y; x = y; // copies all elements from y's storage to x's x(30,40) = y(50,60); // copies 10 elements But then, to be consistent, shouldn't the copy constructor and the copy assignment operator always copy? What about:
array z = x(80,90); // will create an array referencing x's storage In this case, the copy will be elided by the compiler, so no matter what my copy assignment operator does, z will hold a reference to x's storage. There's no way around this.
So does it make more sense for assignment to always create a reference, and copying to be declared explicitly? For example, I could define a wrapper class copy_wrapper, assignment of which forces copying of elements:
class copy_wrapper { array& a_; public: explicit copy_wrapper(array& a) : a_(a) {} array& get() { return a_; } }; class array { // ... array& operator=(const array& a); // references array& operator=(copy_wrapper c); // copies copy_wrapper copy() { return copy_wrapper(*this); } }; This would force users to write:
array x, y; x(30,40) = y(50,60).copy(); // ok, copies x(30,40) = y(50,60); // should this throw a runtime error? x = y; // surprise! x's resource is destructed. A bit cumbersome, and worse than that: not what you expect.
How should I deal with this ambiguity?
array z = x, it's actually the copy constructor which gets called, not the assignment operator. The copy constructor is what you should implement.z=xcopies a reference,z(1,10)=x(1,10)copies a range,z() = x()copies everything.array z = x;is not eligible for copy elision, sincezis not a temporary. I have updated the question accordingly.array z = x(80,90).