3

I would like to understand better the mechanics and the issues behind creating library and I have decided to start from the std::auto_ptr. I am familiar with the syntax and the usage, however I am trying to understand the implementation details. I have implemented my version of the auto_ptr and it looks like this:

#ifndef _AUTO_PTR_H
#define _AUTO_PTR_H

namespace test
{    
    template<typename T>
    class auto_ptr
    {
        public:
            //ctor, dtor, copy ctor and assignment
            explicit auto_ptr(T* ptr = 0) throw() 
                : ptr_(ptr) {}

            auto_ptr(auto_ptr& other) throw()
                :ptr_(other.release()) {}

            auto_ptr<T>& operator=(auto_ptr<T>& other)
            {
                reset(other.release());
                return *this;
            }

            template<typename U>
            auto_ptr<T>& operator=(auto_ptr<U>& other) throw()
            {
                reset(other.release());
                return *this;
            }

            template<typename U>
            auto_ptr(auto_ptr<U>& other) throw() 
                : ptr_(other.release()) {}

            //member
            ~auto_ptr() throw() { delete ptr_; }

            T& operator*() const throw() { return *ptr_; }

            T* operator->() const throw() { return ptr_; }

            T* get() const throw() { return ptr_; }         

            T* release() throw()
            { 
                T* temp = ptr_;
                ptr_ = 0;
                return temp;
            }

            void reset(T* ptr = 0) throw()
            {
                if (ptr_ != ptr)
                {
                    delete ptr_;
                    ptr_ = ptr;
                }
            }

        private:
            T* ptr_;
    };
}

#endif

My class is doing pretty much the job then I have read this question and it is clear why auto_ptr_ref is there for. However I would like to have an actual example when the auto_ptr behaves differently without add auto_ptr_ref.

My implementation works correctly with this functions:

template <typename T>
test::auto_ptr<T> source()
{
    return test::auto_ptr<T>(new T());
}

template <typename T>
void sink(test::auto_ptr<T> bye)
{
}

template <typename T>
void useButNotSink(const test::auto_ptr<T> seeYouLater)
{
    std::cout << *seeYouLater << std::endl;
}

With this test program:

test::auto_ptr<double> copyConstructed(source<double>());

std::cout << *copyConstructed << std::endl;
std::cout << copyConstructed.get() << std::endl;

test::auto_ptr<double> assigned(new double(10));
assigned = source<double>();

std::cout << *assigned << std::endl;
std::cout << assigned.get() << std::endl;
*assigned = 2044;

std::cout << *assigned << std::endl;
useButNotSink(assigned);
sink(assigned);
std::cout << assigned.get() << std::endl;

I am sure I am missing something,can you give me an example where the auto_ptr_ref struct and the conversion operators are mandatory to get the correct behaviour?

Thanks

Community
  • 1
  • 1
Alessandro Teruzzi
  • 3,918
  • 1
  • 27
  • 41
  • 2
    Are you by any chance using an MSVC compiler with settings that allows a temporary to bind to a non-const reference in `auto_ptr& operator=(auto_ptr& other)`? – Bo Persson Jun 07 '11 at 13:49
  • I am using visual studio 2010, but I thought it was irrelevant :-( – Alessandro Teruzzi Jun 07 '11 at 13:54
  • @Bo Persson do you know how to switch off this extension? – Alessandro Teruzzi Jun 07 '11 at 13:57
  • @Allesandro - There is "Disable language extensions" /Za, which breaks most everything else. :-( Otherwise if you use a high enough warning level, like /W4, it will warn you that you are using an extension. – Bo Persson Jun 07 '11 at 14:05
  • @Bo Persson, thanks I will raise my warning level ;-) – Alessandro Teruzzi Jun 07 '11 at 14:07
  • I have a problem with the assignment operator. It does not provide the ["Strong Exception Guarantee"](http://stackoverflow.com/questions/106586/what-are-the-principles-guiding-your-exception-handling-policy/106749#106749) (which std::auto_ptr provides). The call to reset() calls delete which may throw thus causing you to leak the pointer that you get from calling release(). – Martin York Jun 07 '11 at 14:28

2 Answers2

4

You seem to be using compiler extension. I don't see a copy constructor with const reference argument. As you can't pass a temporary to a non const reference,

test::auto_ptr<double> copyConstructed(source<double>());

should fail to compile (the result of source<double>() being a temporary).

(Note that in C++0X, rvalue reference are a better mean to achieve the effect).

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • Hint: Visual C++ is *not* a standard-compliant compiler (and compiles that line). – Jan Hudec Jun 07 '11 at 13:54
  • @Jan, It isn't the only one. SunCC does the same by default. It was the rule a long time ago and compatibility is a stronger constraint for some compilers than others. (I'd prefer an option allowing to get the non conformant but compatible behavior than needing an option to get the standard behavior). – AProgrammer Jun 07 '11 at 13:57
4

Your very first line

test::auto_ptr<double> copyConstructed(source<double>());

is already non-compilable by any standard-compliant compiler. MSVC will allow it when the C++ language extensions are enabled. Disable the extensions and you will immediately realize the need for auto_ptr_ref.

The mechanics behind auto_ptr_ref was too complicated for MSVC 6.0, so in order to support some resemblance of full std::auto_ptr functionality that compiler had to rely on this compiler extension, i.e. allowing non-const references to be bound to temporary objects. The problem was fixed in later versions of the compiler, but this compiler extension remains enabled by default to this day.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765