I write a smart pointer class. When I pass it to a vector, it shows some error message when compiling. But after I delete the explict declaration, it works. What's the error?
explicit shared_ptr(const shared_ptr<T>& sp)
no matching function for call to shared_ptr<int>::shared_ptr(const shared_ptr<int> &)
#include <iostream>
#include <vector>
using namespace std;
template<class T>
class shared_ptr {
private:
struct ptr {
T* ptr;
size_t count;
void release() {
if(-- count == 0) {
delete ptr;
delete this;
}
}
};
private:
ptr* _ptr;
public:
explicit shared_ptr(T* p):
_ptr(new ptr) {
_ptr->ptr = p;
_ptr->count = 1;
}
explicit shared_ptr(const shared_ptr<T>& sp):
//explicit shared_ptr(const shared_ptr& sp):
_ptr(sp._ptr) {
++ _ptr->count;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
_ptr->release();
_ptr = sp._ptr;
++ _ptr->count;
return *this;
}
shared_ptr<T>& operator=(T* p) {
_ptr->release();
_ptr = new ptr;
_ptr->count = 1;
_ptr->ptr = p;
}
T* get() {
return _ptr->ptr;
}
T& operator*() {
return *(_ptr->ptr);
}
T* operator->() {
return _ptr->ptr;
}
~shared_ptr() {
_ptr->release();
}
};
int main() {
vector<shared_ptr<int> > vec;
vec.push_back(shared_ptr<int>(new int(10)));
}
A constructor declared as explicit can only be used when explicitly invoking it. In this line:
vec.push_back(shared_ptr<int>(new int(10)));
You are passing a value to vec which, in a C++03 implementation of the Standard Library, eventually gets copied into the vector, undergoing a copy-initialization such as:
_Tp __x_copy = __x;
The call to the copy constructor here is implicit, but your constructor is marked as explicit. Hence, the error.
Notice that the error only occurs with a C++03 compiler, or when compiling with the -std=c++03 flag, because in C++11 the call to the push_back() function with an rvalue argument (like the temporary you are instantiating) would just end up creating the value in place with an explicit call to the copy constructor.
Therefore, I assume you are working with a C++03 compiler.
Normally, explicit constructors are constructors which take one argument and are not copy-constructors, in order to avoid awkward implicit conversions (actually, in C++11, explicit makes sense also for constructors which take more than one argument, because of copy-initialization through braced initializer lists).
Copy-constructors are usually not declared as explicit.
Related
I'm working with code generator where I can't obtain directly classname of the value which is wrapped in shared_ptr and placed in std::map.
I came to a situation where I need to create new map object but without access to classname I can't perform a valid object constructor call. I tried with the map operator at[], which calls the value constructor, but it calls shared_ptr<T> constructor and the object inside stays uninitialized.
Here the example:
#include <iostream>
#include <map>
#include <memory>
class A
{
public:
A() { std::cout << "A"; }
int getMember() const { return m_member; }
private:
int m_member = 1;
};
int main()
{
std::map<int, A> mapIntToA;
std::map<int, std::shared_ptr<A>> mapIntToAptr;
mapIntToA[1]; // runs A constructor
std::cout << mapIntToA[1].getMember();
mapIntToAptr[1]; // runs shared_ptr constructor
// cant call methods of uninitalized object
// std::cout << mapIntToAptr[1]->getMember();
// this init works, but I can't use classname 'A' in my code generator
// mapIntToAptr[1] = std::make_shared<A>();
return 0;
}
You can use the member types of std::map and std::shared_ptr to get the type of the element.
Something like
using type = typename std::map<int, std::shared_ptr<A>>::mapped_type::element_type;
mapIntToAptr[1] = std::make_shared<type>();
mapIntToAptr.emplace(1, ::std::make_shared<decltype(mapIntToAptr)::mapped_type::element_type>());
Note that use of emplace prevents a situation when map is left with nullptr value when make_shared throws.
operator[] of std::map default constructs absent value.
So, you might wrap std::shared_ptr into a class which constructs you inner class as expected, something like:
template <typename T>
struct shared_ptr_wrapper
{
std::shared_ptr<T> data = std::make_shared<T>();
operator const std::shared_ptr<T>& () const {return data;}
operator std::shared_ptr<T>& () {return data;}
const std::shared_ptr<T>& operator ->() const { return data; }
std::shared_ptr<T>& operator ->() {return data;}
const T& operator *() const { return *data; }
T& operator *() {return *data;}
};
then
std::map<int, shared_ptr_wrapper<A>> mapIntToAptr;
mapIntToAptr[1]; // runs shared_ptr constructor
std::cout << mapIntToAptr[1]->getMember(); // Ok
This is in c++, using Visual Studio 2019 (haven't tried other compilers).
I want to add a templated operator= method. If the parameter is non-const, it works fine. But if the parameter is const, even if I make a version with a const parameter, it isn't called. Instead, it does a simple shallow copy.
If I use a named function instead of the operator, it works as expected. Similarly, if it's not templated, the operator is called as expected. The combo seems to be the issue.
Here's an example that exhibits the issue.
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
// non-const operator=
template<class SrcType>void operator=(SrcType& src)
{
x = src.x;
}
// const operator=
template<class SrcType>void operator=(const SrcType& src)
{
x = src.x;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // ? shallow copy, not calling const operator=
return 0;
}
Any ideas how to get it to use my overloaded function? Thanks.
Your const function template is not called because compiler generated default copy assignment operator by default, which has signature operator=(const CTest&). When compiler must choose between non-template function and template one (when both has the same match), the former is preferred. That is why your templated method is not called.
To help compiler to select version you want, add:
CTest& operator=(const volatile CTest&) = delete;
by above, you disable normal operator=, volatile here is imporant, without it compiler will complain that operator= is disabled. By adding volatile, you just make that template version is better matched than volatile one.
The rest is not changed:
template<class SrcType>
void operator=(SrcType& src)
{
x = src.x;
puts("boom1");
}
template<class SrcType>
void operator=(const SrcType& src) {
x = src.x;
puts("boom2");
}
Demo
If a class does not have a copy-assignment declared, the compiler will generate one implicitly.
You class does not have a copy-assignment operator, it only has a template assignment operator.
When called with a const object the implicitly declared assignment operator is a better match during overload resolution.
The issue here is that we can't delete the implicitly declared assignment operator, since it will then generate a compiler error. We can however write our own assignment operator that forwards to our template.
#include <iostream>
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
CTest(const CTest& v) = delete;
CTest& operator=(const CTest& v) {
return operator=<CTest>(v);
}
// non-const operator=
template<class SrcType>CTest& operator=(SrcType& src)
{
x = src.x;
std::cout << "non-const\n";
return *this;
}
template<class SrcType>CTest& operator=(const SrcType& src)
{
x = src.x;
std::cout << "const\n";
return *this;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // calls copy-assignment and forwards to template
return 0;
}
I'm trying to write a unique_ptr implementation. I'm struggling with writing a move constructor. Here are my problems:
When I mark the move constructor as default, my resource is deleted twice, when I move assign a pointer (auto foo2 = std::move(foo); below) - why?
When I'm trying to assign the underlying pointer in the move constructor like this *rhs = nullptr (see implementation below), the compiler says *rhs is an rvalue and that I cannot assign anything to it.
Finally, rhs.m_ptr = nullptr works. Why does it work, when *rhs = nullptr doesn't?
My code:
#include <iostream>
namespace my
{
template <class T>
class unique_ptr
{
public:
unique_ptr()
{
m_ptr = new T;
}
unique_ptr(const unique_ptr&) = delete;
// move constructor
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
{
m_ptr = *rhs;
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
~unique_ptr()
{
delete m_ptr;
}
T* operator->()
{
return m_ptr;
}
T* operator*()
{
return m_ptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
// no move assignment yet
private:
T* m_ptr;
};
} // namespace my
struct Foo
{
Foo()
{
std::cout << "Foo" << std::endl;
}
~Foo()
{
std::cout << "~Foo" << std::endl;
}
void printHello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
my::unique_ptr<Foo> foo;
foo->printHello();
auto foo2 = std::move(foo);
return 0;
}
On a side note, apparently I can pass a unique_ptr without any template parameter to methods inside the unique_ptr class template. Does compiler just assume it's T?
Please discard any other implementation faults that don't relate to the described problems. It's work in progress.
1) The default move constructor doesn't know about the semantics of your class. So it moves the pointer rhs, but it will not reset the other pointer, which will get deleted as well in the other destructor.
2) *rhs calls operator* and returns a temporary/rvalue T*, a copy of the internal pointer, and is not consistent with the usual operator* which should return a T& or a const T&.
3) see 2. you are returning a temporary object.
So finally, what you should have:
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
T& operator*() {return *m_ptr;}
const T& operator*() const {return *m_ptr;}
And so on.
You're trying too hard. You don't have to go through the external interface. Just assign values:
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
In addition, operator*() should return a T&, not a T*.
I'm implementing a simple smart pointer, which basically keeps track of the number of references to a pointer that it handles.
I know I could implement move semantics, but I don't think it makes sense as copying a smart pointer is very cheap. Especially considering that it introduces opportunities to produce nasty bugs.
Here's my C++11 code (I omitted some inessential code). General comments are welcome as well.
#ifndef SMART_PTR_H_
#define SMART_PTR_H_
#include <cstdint>
template<typename T>
class SmartPtr {
private:
struct Ptr {
T* p_;
uint64_t count_;
Ptr(T* p) : p_{p}, count_{1} {}
~Ptr() { delete p_; }
};
public:
SmartPtr(T* p) : ptr_{new Ptr{p}} {}
~SmartPtr();
SmartPtr(const SmartPtr<T>& rhs);
SmartPtr(SmartPtr<T>&& rhs) =delete;
SmartPtr<T>& operator=(const SmartPtr<T>& rhs);
SmartPtr<T>& operator=(SmartPtr<T>&& rhs) =delete;
T& operator*() { return *ptr_->p_; }
T* operator->() { return ptr_->p_; }
uint64_t Count() const { return ptr_->count_; }
const T* Raw() const { return ptr_->p_; }
private:
Ptr* ptr_;
};
template<typename T>
SmartPtr<T>::~SmartPtr() {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = nullptr;
}
template<typename T>
SmartPtr<T>::SmartPtr(const SmartPtr<T>& rhs) : ptr_{rhs.ptr_} {
++ptr_->count_;
}
template<typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr<T>& rhs) {
if (this != &rhs) {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = rhs.ptr_;
++ptr_->count_;
}
return *this;
}
#endif // SMART_PTR_H_
Guideline
Never delete the special move members.
In typical code (such as in your question), there are two motivations to delete the move members. One of those motivations produces incorrect code (as in your example), and for the other motivation the deletion of the move members is redundant (does no harm nor good).
If you have a copyable class and you don't want move members, simply don't declare them (which includes not deleting them). Deleted members are still declared. Deleted members participate in overload resolution. Members not present don't. When you create a class with a valid copy constructor and a deleted move member, you can't return it by value from a function because overload resolution will bind to the deleted move member.
Sometimes people want to say: this class is neither movable nor copyable. It is correct to delete both the copy and the move members. However just deleting the copy members is sufficient (as long as the move members are not declared). Declared (even deleted) copy members inhibit the compiler from declaring move members. So in this case the deleted move members are simply redundant.
If you declare deleted move members, even if you happen to pick the case where it is redundant and not incorrect, every time someone reads your code, they need to re-discover if your case is redundant or incorrect. Make it easier on readers of your code and never delete the move members.
The incorrect case:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
// ...
};
Here is example code you probably expected to work with CopyableButNotMovble but will fail at compile time:
#include <algorithm>
#include <vector>
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
CopyableButNotMovble(int);
// ...
friend bool operator<(CopyableButNotMovble const& x, CopyableButNotMovble const& y);
};
int
main()
{
std::vector<CopyableButNotMovble> v{3, 2, 1};
std::sort(v.begin(), v.end());
}
In file included from test.cpp:1:
algorithm:3932:17: error: no
matching function for call to 'swap'
swap(*__first, *__last);
^~~~
algorithm:4117:5: note: in
instantiation of function template specialization 'std::__1::__sort<std::__1::__less<CopyableButNotMovble,
CopyableButNotMovble> &, CopyableButNotMovble *>' requested here
__sort<_Comp_ref>(__first, __last, __comp);
^
algorithm:4126:12: note: in
instantiation of function template specialization 'std::__1::sort<CopyableButNotMovble *,
std::__1::__less<CopyableButNotMovble, CopyableButNotMovble> >' requested here
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
^
...
(many nasty error messages from deep inside your std::lib)
The correct way to do this is:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
// ...
};
The redundant case:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble(NeitherCopyableNorMovble&&) = delete;
NeitherCopyableNorMovble& operator=(NeitherCopyableNorMovble&&) = delete;
// ...
};
The more readable way to do this is:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
// ...
};
It helps if you make a practice of always grouping all 6 of your special members near the top of your class declaration, in the same order, skipping those you don't want to declare. This practice makes it easier for readers of your code to quickly determine that you have intentionally not declared any particular special member.
For example, here is the pattern I follow:
class X
{
// data members:
public:
// special members
~X();
X();
X(const X&);
X& operator=(const X&);
X(X&&);
X& operator=(X&&);
// Constructors
// ...
};
Here is a more in-depth explanation of this declaration style.
I am maintaining a project that can take a considerable time to build so am trying to reduce dependencies where possible. Some of the classes could make use if the pImpl idiom and I want to make sure I do this correctly and that the classes will play nicely with the STL (especially containers.) Here is a sample of what I plan to do - does this look OK? I am using std::auto_ptr for the implementation pointer - is this acceptable? Would using a boost::shared_ptr be a better idea?
Here is some code for a SampleImpl class that uses classes called Foo and Bar:
// SampleImpl.h
#ifndef SAMPLEIMPL_H
#define SAMPLEIMPL_H
#include <memory>
// Forward references
class Foo;
class Bar;
class SampleImpl
{
public:
// Default constructor
SampleImpl();
// Full constructor
SampleImpl(const Foo& foo, const Bar& bar);
// Copy constructor
SampleImpl(const SampleImpl& SampleImpl);
// Required for std::auto_ptr?
~SampleImpl();
// Assignment operator
SampleImpl& operator=(const SampleImpl& rhs);
// Equality operator
bool operator==(const SampleImpl& rhs) const;
// Inequality operator
bool operator!=(const SampleImpl& rhs) const;
// Accessors
Foo foo() const;
Bar bar() const;
private:
// Implementation forward reference
struct Impl;
// Implementation ptr
std::auto_ptr<Impl> impl_;
};
#endif // SAMPLEIMPL_H
// SampleImpl.cpp
#include "SampleImpl.h"
#include "Foo.h"
#include "Bar.h"
// Implementation definition
struct SampleImpl::Impl
{
Foo foo_;
Bar bar_;
// Default constructor
Impl()
{
}
// Full constructor
Impl(const Foo& foo, const Bar& bar) :
foo_(foo),
bar_(bar)
{
}
};
SampleImpl::SampleImpl() :
impl_(new Impl)
{
}
SampleImpl::SampleImpl(const Foo& foo, const Bar& bar) :
impl_(new Impl(foo, bar))
{
}
SampleImpl::SampleImpl(const SampleImpl& sample) :
impl_(new Impl(*sample.impl_))
{
}
SampleImpl& SampleImpl::operator=(const SampleImpl& rhs)
{
if (this != &rhs)
{
*impl_ = *rhs.impl_;
}
return *this;
}
bool SampleImpl::operator==(const SampleImpl& rhs) const
{
return impl_->foo_ == rhs.impl_->foo_ &&
impl_->bar_ == rhs.impl_->bar_;
}
bool SampleImpl::operator!=(const SampleImpl& rhs) const
{
return !(*this == rhs);
}
SampleImpl::~SampleImpl()
{
}
Foo SampleImpl::foo() const
{
return impl_->foo_;
}
Bar SampleImpl::bar() const
{
return impl_->bar_;
}
You should consider using copy-and-swap for assignment if it's possible that Foo or Bar might throw as they're being copied. Without seeing the definitions of those classes, it's not possible to say whether they can or not. Without seeing their published interface, it's not possible to say whether they will in future change to do so, without you realising.
As jalf says, using auto_ptr is slightly dangerous. It doesn't behave the way you want on copy or assignment. At a quick look, I don't think your code ever allows the impl_ member to be copied or assigned, so it's probably OK.
If you can use scoped_ptr, though, then the compiler will do that tricky job for you of checking that it's never wrongly modified. const might be tempting, but then you can't swap.
There are a couple of problems with the Pimpl.
First of all, though not evident: if you use Pimpl, you will have to define the copy constructor / assignment operator and destructor (now known as "Dreaded 3")
You can ease that by creating a nice template class with the proper semantic.
The problem is that if the compiler sets on defining one of the "Dreaded 3" for you, because you had used forward declaration, it does know how to call the "Dreaded 3" of the object forward declared...
Most surprising: it seems to work with std::auto_ptr most of the times, but you'll have unexpected memory leaks because the delete does not work. If you use a custom template class though, the compiler will complain that it cannot find the needed operator (at least, that's my experience with gcc 3.4.2).
As a bonus, my own pimpl class:
template <class T>
class pimpl
{
public:
/**
* Types
*/
typedef const T const_value;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
/**
* Gang of Four
*/
pimpl() : m_value(new T) {}
explicit pimpl(const_reference v) : m_value(new T(v)) {}
pimpl(const pimpl& rhs) : m_value(new T(*(rhs.m_value))) {}
pimpl& operator=(const pimpl& rhs)
{
pimpl tmp(rhs);
swap(tmp);
return *this;
} // operator=
~pimpl() { delete m_value; }
void swap(pimpl& rhs)
{
pointer temp(rhs.m_value);
rhs.m_value = m_value;
m_value = temp;
} // swap
/**
* Data access
*/
pointer get() { return m_value; }
const_pointer get() const { return m_value; }
reference operator*() { return *m_value; }
const_reference operator*() const { return *m_value; }
pointer operator->() { return m_value; }
const_pointer operator->() const { return m_value; }
private:
pointer m_value;
}; // class pimpl<T>
// Swap
template <class T>
void swap(pimpl<T>& lhs, pimpl<T>& rhs) { lhs.swap(rhs); }
Not much considering boost (especially for the cast issues), but there are some niceties:
proper copy semantic (ie deep)
proper const propagation
You still have to write the "Dreaded 3". but at least you can treat it with value semantic.
EDIT: Spurred on by Frerich Raabe, here is the lazy version, when writing the Big Three (now Four) is a hassle.
The idea is to "capture" information where the full type is available and use an abstract interface to make it manipulable.
struct Holder {
virtual ~Holder() {}
virtual Holder* clone() const = 0;
};
template <typename T>
struct HolderT: Holder {
HolderT(): _value() {}
HolderT(T const& t): _value(t) {}
virtual HolderT* clone() const { return new HolderT(*this); }
T _value;
};
And using this, a true compilation firewall:
template <typename T>
class pimpl {
public:
/// Types
typedef T value;
typedef T const const_value;
typedef T* pointer;
typedef T const* const_pointer;
typedef T& reference;
typedef T const& const_reference;
/// Gang of Five (and swap)
pimpl(): _holder(new HolderT<T>()), _p(this->from_holder()) {}
pimpl(const_reference t): _holder(new HolderT<T>(t)), _p(this->from_holder()) {}
pimpl(pimpl const& other): _holder(other->_holder->clone()),
_p(this->from_holder())
{}
pimpl(pimpl&& other) = default;
pimpl& operator=(pimpl t) { this->swap(t); return *this; }
~pimpl() = default;
void swap(pimpl& other) {
using std::swap;
swap(_holder, other._holder);
swap(_p, other._p)
}
/// Accessors
pointer get() { return _p; }
const_pointer get() const { return _p; }
reference operator*() { return *_p; }
const_reference operator*() const { return *_p; }
pointer operator->() { return _p; }
const_pointer operator->() const { return _p; }
private:
T* from_holder() { return &static_cast< HolderT<T>& >(*_holder)._value; }
std::unique_ptr<Holder> _holder;
T* _p; // local cache, not strictly necessary but avoids indirections
}; // class pimpl<T>
template <typename T>
void swap(pimpl<T>& left, pimpl<T>& right) { left.swap(right); }
I've been struggling with the same question. Here's what I think the answer is:
You can do what you are suggesting, so long as you define the copy and assignment operators to do sensible things.
It's important to understand that the STL containers create copies of things. So:
class Sample {
public:
Sample() : m_Int(5) {}
void Incr() { m_Int++; }
void Print() { std::cout << m_Int << std::endl; }
private:
int m_Int;
};
std::vector<Sample> v;
Sample c;
v.push_back(c);
c.Incr();
c.Print();
v[0].Print();
The output from this is:
6
5
That is, the vector has stored a copy of c, not c itself.
So, when you rewrite it as a PIMPL class, you get this:
class SampleImpl {
public:
SampleImpl() : pimpl(new Impl()) {}
void Incr() { pimpl->m_Int++; }
void Print() { std::cout << m_Int << std::endl; }
private:
struct Impl {
int m_Int;
Impl() : m_Int(5) {}
};
std::auto_ptr<Impl> pimpl;
};
Note I've mangled the PIMPL idiom a bit for brevity. If you try to push this into a vector, it still tries to create a copy of the SampleImpl class. But this doesn't work, because std::vector requires that the things it store provide a copy constructor that doesn't modify the thing it's copying.
An auto_ptr points to something that is owned by exactly one auto_ptr. So when you create a copy of an auto_ptr, which one now owns the underlying pointer? The old auto_ptr or the new one? Which one is responsible for cleaning up the underlying object? The answer is that ownership moves to the copy and the original is left as a pointer to nullptr.
What auto_ptr is missing that prevents its use in a vector is copy constructor taking a const reference to the thing being copied:
auto_ptr<T>(const auto_ptr<T>& other);
(Or something similar - can't remember all the template parameters). If auto_ptr did provide this, and you tried to use the SampleImpl class above in the main() function from the first example, it would crash, because when you push c into the vector, the auto_ptr would transfer ownership of pimpl to the object in the vector and c would no longer own it. So when you called c.Incr(), the process would crash with a segmentation fault on the nullptr dereference.
So you need to decide what the underlying semantics of your class are. If you still want the 'copy everything' behaviour, then you need to provide a copy constructor that implements that correctly:
SampleImpl(const SampleImpl& other) : pimpl(new Impl(*(other.pimpl))) {}
SampleImpl& operator=(const SampleImpl& other) { pimpl.reset(new Impl(*(other.pimpl))); return *this; }
Now when you try to take a copy of a SampleImpl, you also get a copy of its Impl struct, owned by the copy SampleImpl. If you're taking an object that had lots of private data members and was used in STL containers and turning it into a PIMPL class, then this is probably what you want, as it provides the same semantics as the original. But note that pushing the object into a vector will be considerably slower as there is now dynamic memory allocation involved in copying the object.
If you decide you don't want this copy behaviour, then the alternative is for the copies of SampleImpl to share the underlying Impl object. In this case, it's not longer clear (or even well-defined) which SampleImpl object owns the underlying Impl. If ownership doesn't clearly belong in one place, then std::auto_ptr is the wrong choice for storing it
and you need to use something else, probably a boost template.
Edit: I think the above copy constructor and assignment operator are exception-safe so long as ~Impl doesn't throw an exception. This should always be true of your code anyway.