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,889
  • 1
  • 27
  • 40
  • 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
  • 49,582
  • 8
  • 87
  • 140
  • 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
  • 302,239
  • 39
  • 506
  • 752