assignment of class with const member - c++

Consider the following code:
struct s
{
const int id;
s(int _id):
id(_id)
{}
};
// ...
vector<s> v; v.push_back(s(1));
I get a compiler error that 'const int id' cannot use default assignment operator.
Q1. Why does push_back() need an assignment operator?
A1. Because the current c++ standard says so.
Q2. What should I do?
I don't want to give up the const specifier
I want the data to be copied
A2. I will use smart pointers.
Q3. I came up with a "solution", which seems rather insane:
s& operator =(const s& m)
{
if(this == &m) return *this;
this->~s();
return *new(this) s(m);
}
Should I avoid this, and why (if so)? Is it safe to use placement new if the object is on the stack?

C++03 requires that elements stored in containers be CopyConstructible and Assignable (see §23.1). So implementations can decide to use copy construction and assignment as they see fit. These constraints are relaxed in C++11. Explicitly, the push_back operation requirement is that the type be CopyInsertable into the vector (see §23.2.3 Sequence Containers)
Furthermore, C++11 containers can use move semantics in insertion operations and do on.

I don't want to give up the const specifier
Well, you have no choice.
s& operator =(const s& m) {
return *new(this) s(m);
}
Undefined behaviour.
There's a reason why pretty much nobody uses const member variables, and it's because of this. There's nothing you can do about it. const member variables simply cannot be used in types you want to be assignable. Those types are immutable, and that's it, and your implementation of vector requires mutability.

s& operator =(const s& m)
{
if(this == &m) return *this;
this->~s();
return *new(this) s(m);
}
Should I avoid this, and why (if so)? Is it safe to use placement new if the object is on the stack?
You should avoid it if you can, not because it is ill-formed, but because it is quite hard for a reader to understand your goal and trust in this code. As a programmer, you should aim to reduce the number of WTF/line of code you write.
But, it is legal. According to
[new.delete.placement]/3
void* operator new(std::size_t size, void* ptr) noexcept;
3 Remarks: Intentionally performs no other action.
Invoking the placement new does not allocate or deallocate memory, and is equivalent to manually call the copy constructor of s, which according to [basic.life]/8 is legal if s has a trivial destructor.

Ok,
You should always think about a problem with simple steps.
std::vector<typename T>::push_back(args);
needs to reserve space in the vector data then assigns(or copy, or move) the value of the parameter to memory of the vector.data()[idx] at that position.
to understand why you cannot use your structure in the member function std::vector::push_back , try this:
std::vector<const int> v; // the compiler will hate you here,
// because this is considered ill formed.
The reason why is ill formed, is that the member functions of the class std::vector could call the assignment operator of its template argument, but in this case it's a constant type parameter "const int" which means it doesn't have an assignment operator ( it's none sense to assign to a const variable!!).
the same behavior is observed with a class type that has a const data member. Because the compiler will delete the default assignment operator, expel
struct S
{
const int _id; // automatically the default assignment operator is
// delete i.e. S& operator-(const S&) = delete;
};
// ... that's why you cannot do this
std::vector<S> v;
v.Push_back(S(1234));
But if you want to keep the intent and express it in a well formed code this is how you should do it:
class s
{
int _id;
public:
explicit s(const int& id) :
_id(id)
{};
const int& get() const
{
return _id;
}; // no user can modify the member variable after it's initialization
};
// this is called data encapsulation, basic technique!
// ...
std::vector<S> v ;
v.push_back(S(1234)); // in place construction
If you want to break the rules and impose an assignable constant class type, then do what the guys suggested above.

Q2. What should I do?
Store pointers, preferably smart.
vector<unique_ptr<s>> v;
v.emplace_back(new s(1));

It's not really a solution, but a workaround:
#include <vector>
struct s
{
const int id;
s(int _id):
id(_id)
{}
};
int main(){
std::vector<s*> v;
v.push_back(new s(1));
return 0;
}
This will store pointers of s instead of the object itself. At least it compiles... ;)
edit: you can enhance this with smart c++11 pointers. See Benjamin Lindley's answer.

Use a const_cast in the assignment operator:
S& operator=(const S& rhs)
{
if(this==&rhs) return *this;
int *pid=const_cast<int*>(&this->id);
*pid=rhs.id;
return *this;
}

Related

Why can't a std::vector hold constant objects?

I am not sure why the following code is not allowed to exist:
int main()
{
std::vector<const int> v;
v.reserve(2);
v.emplace_back(100);
v.emplace_back(200);
}
In theory, reserve() does not construct anything, as opposite to resize(). Also, emplace_back() is constructing "in place" objects, so none of this code is writing over an already constructed constant object.
Despite that, even writing the first line, std::vector<const int> v;, is already resulting in compilation error. Why is not allowed at all to have a std::vector of constants?
While the "why" is best read here there are some easy ways to get what you want:
template<class T>
class as_const {
T t;
public:
as_const(T& t_): t(t_) {}
as_const(const as_const&) = default;
as_const(as_const &&) = delete;
as_const& operator=(const as_const&) = default;
as_const& operator=(as_const&&) = delete;
operator const T&() const {
return t;
}
const T& operator*() const {
// Or just a get method, anyway it's nicer to also have an explicit getter
return t;
}
};
std::vector<as_const<int>> vec;
vec.reserve(2)
vec.emplace_back(100);
vec.emplace_back(200);
You even can decide "how constant" your wrapper should be and provide the move constructors (note that t is not constant per-se in as_const) if you think that is reasonable for your use-case.
Note that you cannot prohibit the reallocation of your vector in this way. If you want to do that (and don't want to use a compile-time size array since you now your size only at runtime) take a look at std::unique_ptr<T[]>. But note that in this case you first have to create a mutable variant and then reseat it in a const variant, since the underlying array gets default initialized and you cannot change anything after that.
Of course there is also the possibility of working with an allocator and disallowing a reallocation. But that has no stl-implementation. There are some implementations for this type of behavior out there (I myself gave it a try once but that is a bit of a mess) but I do not know if there is anything in boost.
As the link provided by Bathsheba tells you, the problem is that std::vector<T> is really std::vector<T, std::allocator<T>>. Up to C++20, std::allocator<T>::address() had two overloads, one which takes T& and one which takes T const&. The problem with T=const int should be obvious. C++ has no such thing as extra-const, so both overloads are equally good.

implement move constructor & move assignment operator in c++98 for better performance

Can I simulate move constructor & move assignment operator functionality with copy constructor and assignment operator in C++98 to improve the performance whenever i know copy constructor & copy assignment will be called only for temporary object in the code OR i am inserting needle in my eyes?
I have taken two example's one is normal copy constructor & copy assignment operator and other one simulating move constructor & move assignment operator and pushing 10000 elements in the vector to call copy constructor.
Example(copy.cpp) of normal copy constructor & copy assignment operator
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class MemoryBlock
{
public:
// Simple constructor that initializes the resource.
explicit MemoryBlock(int length)
: _length(length)
, _data(new int[length])
{
}
// Destructor.
~MemoryBlock()
{
if (_data != NULL)
{
// Delete the resource.
delete[] _data;
}
}
//copy constructor.
MemoryBlock(const MemoryBlock& other): _length(other._length)
, _data(new int[other._length])
{
std::copy(other._data, other._data + _length, _data);
}
// copy assignment operator.
MemoryBlock& operator=(MemoryBlock& other)
{
//implementation of copy assignment
}
private:
int _length; // The length of the resource.
int* _data; // The resource.
};
int main()
{
// Create a vector object and add a few elements to it.
vector<MemoryBlock> v;
for(int i=0; i<10000;i++)
v.push_back(MemoryBlock(i));
// Insert a new element into the second position of the vector.
}
Example(move.cpp) of simulated move constructor & move assignment operator functionality with copy constructor & copy assignment operator
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class MemoryBlock
{
public:
// Simple constructor that initializes the resource.
explicit MemoryBlock(int length=0)
: _length(length)
, _data(new int[length])
{
}
// Destructor.
~MemoryBlock()
{
if (_data != NULL)
{
// Delete the resource.
delete[] _data;
}
}
// Move constructor.
MemoryBlock(const MemoryBlock& other)
{
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
(const_cast<MemoryBlock&>(other))._data = NULL;
//other._data=NULL;
}
// Move assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
//Implementation of move constructor
return *this;
}
private:
int _length; // The length of the resource.
int* _data; // The resource.
};
int main()
{
// Create a vector object and add a few elements to it.
vector<MemoryBlock> v;
for(int i=0; i<10000;i++)
v.push_back(MemoryBlock(i));
// Insert a new element into the second position of the vector.
}
I observed performance is improved with some cost:
$ g++ copy.cpp -o copy
$ time ./copy
real 0m0.155s
user 0m0.069s
sys 0m0.085s
$ g++ move.cpp -o move
$ time ./move
real 0m0.023s
user 0m0.013s
sys 0m0.009s
We can observe that performance is increased with some cost.
Has any pitfall to implement move constructor and move assignment
operator simulated functionality in c++98, even I am sure that copy
constructor & assignment only call when temporary objects are
created?
Has there any other way/technique to implement the move constructor
and assignment operator in c++98?
You will not be able to have the language understand R-values in the same way that C++11 and above will, but you can still approximate the behavior of move semantics by creating a custom "R-Value" type to simulate ownership transferring.
The Approach
"Move semantics" is really just destructively editing/stealing the contents from a reference to an object, in a form that is idiomatic. This is contrary to copying from immutable views to an object. The idiomatic approach introduced at the language level in C++11 and above is presented to us as an overload set, using l-values for copies (const T&), and (mutable) r-values for moves (T&&).
Although the language provides deeper hooks in the way that lifetimes are handled with r-value references, we can absolutely simulate the move-semantics in C++98 by creating an rvalue-like type, but it will have a few limitations. All we need is a way to create an overload set that can disambiguate the concept of copying, from the concept of moving.
Overload sets are nothing new to C++, and this is something that can be accomplished by a thin wrapper type that allows disambiguating overloads using tag-based dispatch.
For example:
// A type that pretends to be an r-value reference
template <typename T>
class rvalue {
public:
explicit rvalue(T& ref)
: _ref(&ref)
{
}
T& get() const {
return *_ref;
}
operator T&() const {
return *_ref;
}
private:
T* _ref;
};
// returns something that pretends to be an R-value reference
template <typename T>
rvalue<T> move(T& v)
{
return rvalue<T>(v);
}
We won't be able behave exactly like a reference by accessing members by the . operator, since that functionality does not exist in C++ -- hence having get() to get the reference. But we can signal a means that becomes idiomatic in the codebase to destructively alter types.
The rvalue type can be more creative based on whatever your needs are as well -- I just kept it simple for brevity. It might be worthwhile to add operator-> to at least have a way to directly access members.
I have left out T&& -> const T&& conversion, T&& to U&& conversion (where U is a base of T), and T&& reference collapsing to T&. These things can be introduced by modifying rvalue with implicit conversion operators/constructors (but might require some light-SFINAE). However, I have found this rarely necessary outside of generic programming. For pure/basic "move-semantics", this is effectively sufficient.
Integrating it all together
Integrating this "rvalue" type is as simple as adding an overload for rvalue<T> where T is the type being "moved from". With your example above, it just requires adding a constructor / move assignment operator:
// Move constructor.
MemoryBlock(rvalue<MemoryBlock> other)
: _length(other.get()._length),
_data(other.get()._data)
{
other.get()._data = NULL;
}
MoveBlock& operator=(rvalue<MemoryBlock> other)
{
// same idea
}
This allows you to keep copy constructors idiomatic, and simulate "move" constructors.
The use can now become:
MemoryBlock mb(42);
MemoryBlock other = move(mb); // 'move' constructor -- no copy is performed
Here's a working example on compiler explorer that compares the copy vs move assemblies.
Limitations
No PR-values to rvalue conversion
The one notable limitation of this approach, is that you cannot do PR-value to R-value conversions that would occur in C++11 or above, like:
MemoryBlock makeMemoryBlock(); // Produces a 'PR-value'
...
// Would be a move in C++11 (if not elided), but would be a copy here
MemoryBlock other = makeMemoryBlock();
As far as I am aware, this cannot be replicated without language support.
No auto-generated move-constructors/assignment
Unlike C++11, there will be no auto-generated move constructors or assignment operators -- so this becomes a manual effort for types that you want to add "move" support to.
This is worth pointing out, since copy constructors and assignment operators come for free in some cases, whereas move becomes a manual effort.
An rvalue is not an L-value reference
In C++11, a named R-value reference is an l-value reference. This is why you see code like:
void accept(T&& x)
{
pass_to_something_else(std::move(x));
}
This named r-value to l-value conversion cannot be modeled without compiler support. This means that an rvalue reference will always behave like an R-value reference. E.g.:
void accept(rvalue<T> x)
{
pass_to_something_else(x); // still behaves like a 'move'
}
Conclusion
So in short, you won't be able to have full language support for things like PR-values. But you can, at least, implement a means of allowing efficient moving of the contents from one type to another with a "best-effort" attempt. If this gets adopted unanimously in a codebase, it can become just as idiomatic as proper move-semantics in C++11 and above.
In my opinion, this "best-effort" is worth it despite the limitations listed above, since you can still transfer ownership more efficiently in an idiomatic manner.
Note: I do not recommend overloading both T& and const T& to attempt "move-semantics". The big issue here is that it can unintentionally become destructive with simple code, like:
SomeType x; // not-const!
SomeType y = x; // x was moved?
This can cause buggy behavior in code, and is not easily visible. Using a wrapper approach at least makes this destruction much more explicit

Is the copy and swap idiom still useful in C++11

I refer to this question:
What is the copy-and-swap idiom?
Effectively, the above answer leads to the following implementation:
class MyClass
{
public:
friend void swap(MyClass & lhs, MyClass & rhs) noexcept;
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};
void swap( MyClass & lhs, MyClass & rhs )
{
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
}
However, notice that we could eschew the swap() altogether, doing the following:
class MyClass
{
public:
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { *this = std::forward<MyClass>(rhs); }
MyClass & operator=(MyClass rhs)
{
/* put swap code here */
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
// :::
return *this;
}
};
Note that this means that we will no longer have a valid argument dependent lookup on std::swap with MyClass.
In short is there any advantage of having the swap() method.
edit:
I realized there is a terrible mistake in the second implementation above, and its quite a big thing so I will leave it as-is to instruct anybody who comes across this.
if operator = is defined as
MyClass2 & operator=(MyClass2 rhs)
Then whenever rhs is a r-value, the move constructor will be called. However, this means that when using:
MyClass2(MyClass2 && rhs)
{
//*this = std::move(rhs);
}
Notice you end up with a recursive call to the move constructor, as operator= calls the move constructor...
This is very subtle and hard to spot until you get a runtime stack overflow.
Now the fix to that would be to have both
MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)
this allows us to define the copy constructors as
MyClass2(const MyClass2 & rhs)
{
operator=( rhs );
}
MyClass2(MyClass2 && rhs)
{
operator=( std::move(rhs) );
}
Notice that you write the same amount of code, the copy constructors come "for-free" and you just write operator=(&) instead of the copy constructor and operator=(&&) instead of the swap() method.
First of all, you're doing it wrong anyway. The copy-and-swap idiom is there to reuse the constructor for the assignment operator (and not the other way around), profiting from already properly constructing constructor code and guaranteeing strong exception safety for the assignment operator. But you don't call swap in the move constructor. In the same way the copy constructor copies all data (whatever that means in the given context of an individual class), the move constructor moves this data, your move constructor constructs and assigns/swaps:
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
And this would in your alternative version just be
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { using std::swap; swap(x, rhs.x); return *this; }
Which doesn't exhibit the severe error introduced by calling the assignment operator inside the constructor. You should never ever call the assignment operator or swap the whole object inside a constructor. Constructors are there to care for construction and have the advantage of not having to care for the, well, destruction of the previous data, since that data doesn't exist yet. And likewise can constructors handle types not default constructible and last but not least often direct construction can be more performant than defualt construction followed by assignment/swap.
But to answer your question, this whole thing is still the copy-and-swap idiom, just without an explicit swap function. And in C++11 it is even more useful because now you have implemented both copy and move assignment with a single function.
If the swap function is still of value outside of the assignment operator is an entirely different question and depends if this type is likely to be swapped, anyway. In fact in C++11 types with proper move semantics can just be swapped sufficiently efficient using the default std::swap implementation, often eliminating the need for an additional custom swap. Just be sure not to call this default std::swap inside of your assignment operator, since it does a move assignment itself (which would lead to the same problems as your wrong implementation of the move constructor).
But to say it again, custom swap function or not, this doesn't change anything in the usefulness of the copy-and-swap idiom, which is even more useful in C++11, eliminating the need to implement an additional function.
You're certainly not considering the whole picture. Your code re-uses constructors for different assignment operators, the original re-uses assignment operators for different constructors. This is basically the same thing, all you've done is shift it around.
Except that since they write constructors, they can deal with non-default-constructible types or types whose values are bad if not initialized explicitly like int or are plain expensive to default-construct or where the default-constructed members are not valid to destruct (for example, consider a smart pointer- an uninitialized T* leads to a bad delete).
So basically, all you've achieved is the same principle but in a decidedly worse place. Oh, and you had to define all four functions, else mutual recursion, whereas the original copy-and-swap only defined three functions.
The validity of the reasons (if any) to use the copy-and-swap idiom to implement copy assignment are the same in C++11 as they are in previous versions.
Also note that you should use std::move on member variables in the move constructor, and you should use std::move on any rvalue references that are function parameters.
std::forward should only be used for template parameter references of the form T&& and also auto&& variables (which both may be subject to reference folding into lvalue references during type deduction) to preserve their rvalueness or lvalueness as appropriate.

std::vector of objects and const-correctness

Consider the following:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
Obviously, the copy constructor is not enough. What am I missing?
EDIT:
Ofc. I cannot change this->c in operator=() method, so I don't see how operator=() would be used (although required by std::vector).
I'm not sure why nobody said it, but the correct answer is to drop the const, or store A*'s in the vector (using the appropriate smart pointer).
You can give your class terrible semantics by having "copy" invoke UB or doing nothing (and therefore not being a copy), but why all this trouble dancing around UB and bad code? What do you get by making that const? (Hint: Nothing.) Your problem is conceptual: If a class has a const member, the class is const. Objects that are const, fundamentally, cannot be assigned.
Just make it a non-const private, and expose its value immutably. To users, this is equivalent, const-wise. It allows the implicitly generated functions to work just fine.
An STL container element must be copy-constructible and assignable1(which your class A isn't). You need to overload operator =.
1
: §23.1 says The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignabletypes
EDIT :
Disclaimer: I am not sure whether the following piece of code is 100% safe. If it invokes UB or something please let me know.
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
EDIT 2
I think the above code snippet invokes Undefined Behaviour because trying to cast away the const-ness of a const qualified variable invokes UB.
You're missing an assignment operator (or copy assignment operator), one of the big three.
The stored type must meet the CopyConstructible and Assignable requirements, which means that operator= is needed too.
Probably the assignment operator. The compiler normally generates a default one for you, but that feature is disabled since your class has non-trivial copy semantics.
I think the STL implementation of vector functions you are using require an assignment operator (refer Prasoon's quote from the Standard). However as per the quote below, since the assignment operator in your code is implicitly defined (since it is not defined explicitly), your program is ill-formed due to the fact that your class also has a const non static data member.
C++03
$12.8/12 - "An implicitly-declared
copy assignment operator is implicitly
defined when an object of its class
type is assigned a value of its class
type or a value of a class type
derived from its class type. A program
is illformed if the class for which a
copy assignment operator is implicitly
defined has:
— a nonstatic data member of const type, or
— a nonstatic data
member of reference type, or
— a
nonstatic data member of class type
(or array thereof) with an
inaccessible copy assignment operator,
or
— a base class with an inaccessible
copy assignment operator.
Workaround without const_cast.
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
I recently ran into the same situation and I used a std::set instead, because its mechanism for adding an element (insert) does not require the = operator (uses the < operator), unlike vector's mechanism (push_back).
If performance is a problem you may try unordered_set or something else similar.
You also need to implement a copy constructor, which will look like this:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
The special const_cast template takes a pointer type and casts it back to a writeable form, for occasions such as this.
It should be noted that const_cast is not always safe to use, see here.
I just want to point out that as of C++11 and later, the original code in the question compiles just fine! No errors at all. However, vec.emplace_back() would be a better call, as it uses "placement new" internally and is therefore more efficient, copy-constructing the object right into the memory at the end of the vector rather than having an additional, intermediate copy.
cppreference states (emphasis added):
std::vector<T,Allocator>::emplace_back
Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Here's a quick demo showing that both vec.push_back() and vec.emplace_back() work just fine now.
Run it here: https://onlinegdb.com/BkFkja6ED.
#include <cstdio>
#include <vector>
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
// Copy constructor
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
A foo2(999);
std::vector<A> vec;
vec.push_back(foo); // works!
vec.emplace_back(foo2); // also works!
for (size_t i = 0; i < vec.size(); i++)
{
printf("vec[%lu].c = %i\n", i, vec[i].c);
}
return 0;
}
Output:
vec[0].c = 1337
vec[1].c = 999

Is this C++ pointer container safe?

I want a safe C++ pointer container similar to boost's scoped_ptr, but with value-like copy semantics. I intend to use this for a very-rarely used element of very-heavily used class in the innermost loop of an application to gain better memory locality. In other words, I don't care about performance of this class so long as its "in-line" memory load is small.
I've started out with the following, but I'm not that adept at this; is the following safe? Am I reinventing the wheel and if so, where should I look?
template <typename T>
class copy_ptr {
T* item;
public:
explicit copy_ptr() : item(0) {}
explicit copy_ptr(T const& existingItem) : item(new T(existingItem)) {}
copy_ptr(copy_ptr<T> const & other) : item(new T(*other.item)) {}
~copy_ptr() { delete item;item=0;}
T * get() const {return item;}
T & operator*() const {return *item;}
T * operator->() const {return item;}
};
Edit: yes, it's intentional that this behaves pretty much exactly like a normal value. Profiling shows that the algorithm is otherwise fairly efficient but is sometimes hampered by cache misses. As such, I'm trying to reduce the size of the object by extracting large blobs that are currently included by value but aren't actually used in the innermost loops. I'd prefer to do that without semantic changes - a simple template wrapper would be ideal.
No it is not.
You have forgotten the Assignment Operator.
You can choose to either forbid assignment (strange when copying is allowed) by declaring the Assignment Operator private (and not implementing it), or you can implement it thus:
copy_ptr& operator=(copy_ptr const& rhs)
{
using std::swap;
copy_ptr tmp(rhs);
swap(this->item, tmp.item);
return *this;
}
You have also forgotten in the copy constructor that other.item may be null (as a consequence of the default constructor), pick up your alternative:
// 1. Remove the default constructor
// 2. Implement the default constructor as
copy_ptr(): item(new T()) {}
// 3. Implement the copy constructor as
copy_ptr(copy_ptr const& rhs): item(other.item ? new T(*other.item) : 0) {}
For value-like behavior I would prefer 2, since a value cannot be null. If you go for allowing nullity, introduces assert(item); in both operator-> and operator* to ensure correctness (in debug mode) or throw an exception (whatever you prefer).
Finally the item = 0 in the destructor is useless: you cannot use the object once it's been destroyed anyway without invoking undefined behavior...
There's also Roger Pate's remark about const-ness propagation to be more "value-like" but it's more a matter of semantics than correctness.
You should "pass on" the const-ness of the copy_ptr type:
T* get() { return item; }
T& operator*() { return *item; }
T* operator->() { return item; }
T const* get() const { return item; }
T const& operator*() const { return *item; }
T const* operator->() const { return item; }
Specifying T isn't needed in the copy ctor:
copy_ptr(copy_ptr const &other) : item (new T(*other)) {}
Why did you make the default ctor explicit? Nulling the pointer in the dtor only makes sense if you plan on UB somewhere...
But these are all minor issues, what you have there is pretty much it. And yes, I've seen this invented many times over, but people tend to tweak the semantics just slightly each time. You might look at boost::optional, as that's almost what you have written here as you present it, unless you're adding move semantics and other operations.
In addition to what Roger has said, you can Google 'clone_ptr' for ideas/comparisons.