Search code examples
c++templatesunique-ptrc++03

Writing partial unique_ptr for C++03 compiler which operates on common codebase with newer compilers


I need to write an implementation of unique_ptr for C++03 which would only absorb changes made in common codebase under other platforms where C++11 is enabled. Namely, it doesn't need to check anything, everything will be checked by other platforms compilers. But it needs to properly pass and destroy objects.

This is an example of testing code. And full test online.

template <class T> std2::unique_ptr<T> pass_through(std2::unique_ptr<T> p) { return p; }
...
std2::unique_ptr<A> a1 = std2::make_unique<B>();
std2::unique_ptr<A> a2 = std2::move(pass_through(a1));                             // doesn't work
std2::unique_ptr<A> a2 = (const std2::unique_ptr<A>&)std2::move(pass_through(a1)); // doesn't work
std2::unique_ptr<A> a2 = (const std2::unique_ptr<B>&)std2::move(pass_through(a1)); // works
std2::unique_ptr<B> a2 = std2::move(pass_through(a1));                             // works

unique_ptr implementation:

class Num { protected: static int i; }; int Num::i = 0;

template<class Data>
class unique_ptr : Num
{
    int _i = i++;
    mutable Data *_data;

public:
    explicit unique_ptr(Data* data = nullptr) 
    { _data = data; std::cout << _i << ": cons" << std::endl; }

    template<class T>
    unique_ptr(unique_ptr<T>& other) :
        _data((Data*)other.release())
    { std::cout << _i << ": copy cons <T>" << std::endl; }

    template<class T>
    unique_ptr(const unique_ptr<T>& other) :
        _data((Data*)other.release())
    { std::cout << _i << ": copy cons const <T>" << std::endl; }

    ~unique_ptr() { std::cout << _i << ": deleting :" << _data << std::endl; delete _data; _data = 0; }

    unique_ptr& operator=(Data* data)
    { reset(data); return *this; }

    template<class T>
    unique_ptr& operator=(unique_ptr<T>& other) {
        std::cout << "assign <T>" << std::endl;
        reset((Data*)other.release());
        return *this;
    }

    template<class T>
    unique_ptr& operator=(const unique_ptr<T>& other) {
        std::cout << "assign const <T>" << std::endl;
        reset((Data*)other.release());
        return *this;
    }

    operator bool() const { return _data != nullptr; }

    Data* release() const
    { Data* res = _data; _data = nullptr; return res; }

    template <class T>
    void reset(T* data)
    { Data* old = _data; _data = (Data*)data; delete old; }
};

template <class T>       std2::unique_ptr<T>& move(      std2::unique_ptr<T>& data) { return data; }
template <class T> const std2::unique_ptr<T>& move(const std2::unique_ptr<T>& data) { return data; }

template<class Data>
inline unique_ptr<Data> make_unique() { return unique_ptr<Data>(new Data()); }

Classes:

struct A {
    A() { std::cout << "A()" << std::endl; }
    virtual ~A() { std::cout << "~A()" << std::endl; };
};

struct B : public A {
    B() : A() { std::cout << "B()" << std::endl; }
    ~B() { std::cout << "~B" << std::endl; };
};

Output for the first option. Looks like some additional copying (memcpy) has been done.

A()
B()
0: cons
1: copy cons const <T>
0: deleting :0000000000000000
2: copy cons <T>
Inside pass_through
3: copy cons <T>
2: deleting :0000000000000000
3: deleting :000001CC492B1790   // This one is #3
~B
~A()
3: deleting :000001CC492B1790   // This one is also #3

Could you help me with this double deleting?


Solution

  • Templates are never copy constructors.

    unique_ptr(const unique_ptr<Data>& other)
    unique_ptr(unique_ptr<Data>& other)
    

    write these two.

    Also similar for operator=.