Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

auto_ptr: improved implementation

62 views
Skip to first unread message

Rani Sharoni

unread,
Nov 29, 2003, 8:34:18 PM11/29/03
to
Hello all,

I think that I found an improved and simpler (?) implementation to the
challenging facility auto_ptr. The technique that I used is in fact general
and can be used in other facilities with move semantics.

The limitation of the current auto_ptr is that it can't handle conversion
from auto_ptr<derived> r-value - to auto_ptr<base> in copy initialization
(TC1 DR #84). For example:
struct B {};
struct D : B {};

// copy initialization - case 1
auto_ptr<B> x = auto_ptr<D>();

auto_ptr<D> source();
int sink(auto_ptr<B>);

// copy initialization - case 2
int y = sink( source() );

Excellent analysis of the current auto_ptr was given in the official
proposal:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1997/N1128.pdf

My motivation was to come up with an implementation technique that rejects
source auto_ptr types with constant qualifier (l-values and r-values).

Basic technique, consider the following function templates:
template<typename T> int f(T&, long* = 0);
template<typename T> int f(T const&, char* = 0);

The second dummy parameter purpose is to break the partial order between the
above functions. I claim that the function 'f' accept only non-const values.
There are 3 cases:
1) Non-const l-value - first f has exact match.
2) Non-const r-value - only second f is viable.
3) const value - both f have exact match but are not ordered which lead to
ambiguity.

Given the above property implementing class with move semantics is easy.

auto_ptr implementation (only the relevant parts):
template<class X>
class auto_ptr
{
template<typename T>
struct enforce_auto_ptr; // SFINAE helper

template<typename T>
struct enforce_auto_ptr< auto_ptr<T> >
{
typedef enforce_auto_ptr* type;
};

template<typename T>
struct enforce_auto_ptr< auto_ptr<T> const >
{
typedef enforce_auto_ptr* type;
};

public:
typedef X element_type;

explicit auto_ptr(X* p = 0) throw()
: ptr_(p)
{}

// l-value same type
auto_ptr(auto_ptr& rhs) throw()
: ptr_(rhs.release())
{}

// l-value U == auto_ptr<P> and P != T (SFINAE)
template<class U>
auto_ptr(U& rhs, typename enforce_auto_ptr<U>::type* = 0) throw()
: ptr_(rhs.release())
{}

// non const r-value U == auto_ptr<P> (SFINAE)
template<class U>
auto_ptr(U const& rhs, typename enforce_auto_ptr<U>::type = 0) throw()
: ptr_(const_cast<U&>(rhs).release())
{}

// same as before
auto_ptr& operator=(auto_ptr&) throw();

template<class Y>
auto_ptr& operator=(auto_ptr<Y>&) throw();

~auto_ptr() throw();

// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();

private:
X* ptr_;
};

//
// Test cases
//
struct B {};
struct D : B {};

int main()
{
// r-value: direct init same type
auto_ptr<B> bp1(auto_ptr<B>(0));

// r-value: copy init same type
auto_ptr<B> bp2 = auto_ptr<B>(new D);

// r-value: direct init different types
auto_ptr<B> bp3(auto_ptr<D>(new D));

// r-value: ** copy init different types **
auto_ptr<B> bp4 = auto_ptr<D>(new D);

// l-value: copy init same type
auto_ptr<B> bp5 = bp4;
auto_ptr<D> dp6 = auto_ptr<D>(new D);

// l-value: copy init different type
auto_ptr<B> bp7 = dp6;

auto_ptr<B> const bp10(new D);
// auto_ptr<B> bp11(bp10); // error const l-value

auto_ptr<D> const source1();
//auto_ptr<B> bp12(source1()); // error const r-value
}

The code was tested using Comeau C/C++ 4.3.3 online. VC7.1 totally missed my
intentions while GCC 3.2 accepts const auto_ptr.

As I mentioned in the past, it seems that only few compilers fully
understand initialization in C++.

Cheers,
Rani


---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Rani Sharoni

unread,
Nov 30, 2003, 8:12:28 PM11/30/03
to
"Rani Sharoni" wrote:
> Hello all,
>
> I think that I found an improved and simpler (?) implementation to the
> challenging facility auto_ptr.

In short, the original suggestion is wrong and here is the fix (complete
suggestion below):


template<typename T>
struct enforce_auto_ptr< auto_ptr<T> const >

{ // unconditionally fail the compilation of this instance
typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type;
};

Long story:
24 hours after sending my post I realized that the basic technique I relied
on is wrong since 14.5.5.2/6 says:
The presence of unused ellipsis and default arguments has no effect on the
partial ordering of function templates.

Yet another case in which bug in EDG, VC7.1 and BCC5.6 falsely affected my
intuition. The "correct" intuition is that partial ordering is overloading
of function templates and optional arguments don't effect overloading. In
this case GCC was right.

Check your compiler:
template<typename T> // #1
char* f(T&, char* = 0);

template<typename T> // #2
long* f(T const&, long* = 0);

int const x = 0;
long* p = f(x); // 14.5.5.2/6: calls #2

IMHO The root of all those heroic efforts to implement easy to use move
semantics in C++ is the awful decision of not allowing binding of non-const
r-value to reference of non-const with the *same* type.

I guess that I'm exceptionally "inspired" in those days since I found
*additional* technique that allowed me to implement a correct version of
auto_ptr.
Again, the motivation being the basic technique is to allow only non-const
parameters (i.e. reject const with compilation error):

template<typename T>
struct error_on_const {
typedef int type;
};

template<typename T>
struct error_on_const<T const> {
typedef typename error_on_const<T>::const_type_is_not_allowed
const_type_is_not_allowed;

typedef int type;
};

template<typename T>
long* accept_non_const(T&, typename error_on_const<T>::type = 0);

template<typename T>
char* accept_non_const(T const&);

struct A {} a;

long* x1 = accept_non_const(a);
char* x2 = accept_non_const(A());

A const ca = A();

// char* x3 = accept_non_const(ca);
// instantiation of #1 fails although #2 is better
// IMO 14.7.1/5 doesn't hold since #1 affects overload
// resolution with ill-formed candidate

EDG, GCC and VC7.1 like the code and it can be easily transformed for favor
of compilers that don't support partial specializations (e.g. VC7).

Yet another heroic try to implement auto_ptr:

template<class X>
class auto_ptr
{
template<typename T>
struct enforce_auto_ptr;

template<typename T>


struct enforce_auto_ptr< auto_ptr<T> >
{
typedef enforce_auto_ptr* type;
};

template<typename T>
struct enforce_auto_ptr< auto_ptr<T> const >
{

typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type;
};

public:
typedef X element_type;

explicit auto_ptr(X* p = 0) throw()
: ptr_(p)
{}

// l-value same type
auto_ptr(auto_ptr& rhs) throw()
: ptr_(rhs.release())
{}

// l-value U == auto_ptr<P> and P != T

template<class U>
auto_ptr(U& rhs, typename enforce_auto_ptr<U>::type = 0) throw()
: ptr_(rhs.release())
{}

// r-value U == auto_ptr<P>
template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()
: ptr_(const_cast<auto_ptr<U>&>(rhs).release())
{}

~auto_ptr() throw();

private:
X* ptr_;
};

//auto_ptr<B> bp11(bp10); // error const l-value

auto_ptr<D> const source1();
//auto_ptr<B> bp12(source1()); // error const r-value
}

The code compiled fine using Comeau 4.3.0+ and GCC3.2. VC7.1 has problems
with template "copy" constructor.

Cheers (revised),

Rani Sharoni

unread,
Dec 1, 2003, 12:39:21 PM12/1/03
to
"Rani Sharoni" wrote:
> "Rani Sharoni" wrote:
>> Hello all,
>>
>> I think that I found an improved and simpler (?) implementation to
>> the challenging facility auto_ptr.

Improved auto_ptr - final version:

I hope that I will have time to make it an official proposal.

The new auto_ptr implementation allows all the intuitive usage that the
original suggestions had in mind. The final auto_ptr standard version is
almost perfect but still can't handle construction of auto_ptr from source
with different type in copy initialization context:

struct B {};
struct D : B {};

auto_ptr<B> bp = auto_ptr<D>(); // DR #81

auto_ptr<D> dp;
auto_ptr<B> bp2 = dp; // ambiguity between two possible UDC

The new proposal overcome these limitations and allows the previous
intuitive usage. The main difference from the current auto_ptr is that no
special conversion operators are required and only constructors are used.
I noticed that standard C++ library vendors also enhanced the assignment
capabilities of auto_ptr (i.e. accepts r-values with same and different
types) using additional, non standard, assignment operator that accepts
auto_ptr_ref. I think that this additional functionality was overlooked by
mistake in the standard and I made minor changed in my suggestion to allow
the enhanced assignments cases (i.e. generalized assignment operator accepts
its argument *by-value*).

I tweaked (conditioned with ifdef) the code a bit for the favor of VC7.1+
and now the test code compiles using Comeau 4.3.0+, GCC 3.2 and VC7.1+. I
will appreciate it if someone will try to compile the code using other
advanced C++ compilers.

Final (hopefully) suggestion, comments are more than welcome:

<auto_ptr code>

#include <cassert>

template<class X>
class auto_ptr
{

public:
typedef X element_type;

explicit auto_ptr(X* p = 0) throw()
: ptr_(p)
{}

#ifndef _MSC_VER

// l-value same type - copy constructor
auto_ptr(auto_ptr& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release())
{}

#else
// l-value same type - copy constructor
// the const satisfies VC7.1! but its diagnostic
// might be a pain (fixed in VC8) and member auto_ptr
// will not generate the correct copy constructor
auto_ptr(auto_ptr const& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release())
{}
#endif

// non-const auto_ptr r-value (any U) and l-value (U != T)
// const auto_ptr will result ill-formed instance


template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()
: ptr_(const_cast<auto_ptr<U>&>(rhs).release())
{}

// l-value same type - copy assignment operator
auto_ptr& operator=(auto_ptr& rhs) throw()
{
reset(rhs.release());
return *this;
}

// by-value to allow r-values of the same type
// and (r/l)-value of different types
template<class Y>
auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{
reset(rhs.release());
return *this;
}

// same as before
~auto_ptr() throw()
{
reset();
}

// 20.4.5.2 members:
X& operator* () const throw()
{
assert(ptr_);
return *get();
}

X* operator->() const throw()

{
return &*(*this);
}

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

X* release() throw()
{
X* ptr = get();
ptr_ = 0;
return ptr;
}

void reset(X* p = 0) throw()
{
if (ptr_ != p)
{
delete get();
ptr_ = p;
}
}

private:
X* ptr_;

private:
template<typename T> struct error_const_auto_ptr;

template<typename T> struct error_const_auto_ptr< auto_ptr<T> const >


{ typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };

//
// reject const auto_ptr's with compilation error
// during instantiation of declaration (14.7.1/1)
// other types will trigger SFINAE (14.8.2/2/3/3)
//
template<class U>
auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
};

</auto_ptr code>

//
// Test cases
//
struct B {};
struct D : B {};

template class auto_ptr<B>;
template class auto_ptr<D>;

struct X
{
// implicit ctor - X(X&)
X(X&); // or EDG yield error (fine bug!);
auto_ptr<D> aptr_;
};

auto_ptr<D> source()
{
return auto_ptr<D>(new D);
}

void sink(auto_ptr<B>) {}

auto_ptr<D> const const_source()
{
return source();
}

struct X2
{
operator auto_ptr<B>() const
{
return source();
}
};

int main()
{
// r-value: direct init same type
auto_ptr<B> bp1(auto_ptr<B>(0));

// r-value: copy init same type
auto_ptr<B> bp2 = auto_ptr<B>(new D);

// r-value: direct init different types
auto_ptr<B> bp3(auto_ptr<D>(new D));

// r-value: ** copy init different types **
auto_ptr<B> bp4 = auto_ptr<D>(new D);

sink( source() );

// l-value: copy init same type
auto_ptr<B> bp5 = bp4;
auto_ptr<D> dp6 = auto_ptr<D>(new D);

// l-value: copy init different type
auto_ptr<B> bp7 = dp6;

auto_ptr<B> const bp10(new D);

// auto_ptr<B> bp11(bp10); // error const l-value

// auto_ptr<B> bp12(const_source()); // error const r-value

// new: user defined conversion selected
sink( X2() );

// assignment l-value same type
bp5 = bp4;

// assignment l-value different type
bp5 = dp6;

// new: assignment r-values same type
bp5 = auto_ptr<B>();

// new: assignment r-values different type
bp5 = auto_ptr<D>();
}

Again, Comments are more than welcome.

Thanks,

David Abrahams

unread,
Dec 1, 2003, 9:27:44 PM12/1/03
to
rani_s...@hotmail.com ("Rani Sharoni") writes:

> Final (hopefully) suggestion, comments are more than welcome:

That's wonderful! I sure hope you do submit a paper to the
committee. Actually, I hope you submit a DR which says that auto_ptr
is overspecified, suggesting wording which allows this implementation.

Then I also challenge you to find a way to use this technique in
implementing an auto_ptr which is arguably standard-conforming, even
if the DR is not accepted. In other words, write an auto_ptr which
conforms to both the letter and the spirit of the standard :)

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Hyman Rosen

unread,
Dec 1, 2003, 11:56:50 PM12/1/03
to
Rani Sharoni wrote:
> auto_ptr(auto_ptr& rhs) throw()
> : ptr_(const_cast<auto_ptr&>(rhs).release())
> {}

Why the const_cast here?

Dhruv

unread,
Dec 2, 2003, 1:32:57 PM12/2/03
to
On Mon, 01 Dec 2003 17:39:21 +0000, Rani Sharoni wrote:
[...]

I cannot understand a few things here.


> <auto_ptr code>

I guess this version takes an auto_ptr l-value (non-const)

> // l-value same type - copy constructor
> auto_ptr(auto_ptr& rhs) throw()
> : ptr_(const_cast<auto_ptr&>(rhs).release())
> {}

I have a difficulty in understanding how an r-value can be non-const,
because I understand r-values as values that occur on the right hand side
of expressions, and whwch cannot be assigned to, meanain that they cannot
act as l-values, or it it that every l-value is also an r-value but not
the other way around?

Also, I have difficulty in understand when the const qualifier in the
function parameter stands for 1. The parameter will not be modified in the
function body, and when it stands for 2. I will take only conatant values
as my parameters?

> // non-const auto_ptr r-value (any U) and l-value (U != T)
> // const auto_ptr will result ill-formed instance
> template<class U>
> auto_ptr(auto_ptr<U> const& rhs) throw()
> : ptr_(const_cast<auto_ptr<U>&>(rhs).release())
> {}
>

> private:
> X* ptr_;
>
> private:
> template<typename T> struct error_const_auto_ptr;
>
> template<typename T> struct error_const_auto_ptr< auto_ptr<T> const >
> { typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };
>
> //
> // reject const auto_ptr's with compilation error
> // during instantiation of declaration (14.7.1/1)
> // other types will trigger SFINAE (14.8.2/2/3/3)
> //
> template<class U>
> auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
> };
>
> </auto_ptr code>


Regards,
-Dhruv.

Rani Sharoni

unread,
Dec 2, 2003, 1:44:14 PM12/2/03
to
Hyman Rosen wrote:
> Rani Sharoni wrote:
>> auto_ptr(auto_ptr& rhs) throw()
>> : ptr_(const_cast<auto_ptr&>(rhs).release())
>> {}
>
> Why the const_cast here?

It's an oversight (i.e. cut and paste crime). The cast is not needed.

Thanks,
Rani

Rani Sharoni

unread,
Dec 3, 2003, 12:29:04 AM12/3/03
to
David Abrahams wrote:
> rani_s...@hotmail.com ("Rani Sharoni") writes:
>
>> Final (hopefully) suggestion, comments are more than welcome:
>
> That's wonderful! I sure hope you do submit a paper to the
> committee. Actually, I hope you submit a DR which says that auto_ptr
> is overspecified, suggesting wording which allows this implementation.
>
> Then I also challenge you to find a way to use this technique in
> implementing an auto_ptr which is arguably standard-conforming, even
> if the DR is not accepted. In other words, write an auto_ptr which
> conforms to both the letter and the spirit of the standard :)

I appreciate your feedback.

I understand your concern but in fact my main motivation was to keep the
spirit of the standard auto_ptr and come with an implementation that met
*all* the requirements which motivated by intuitive usage yet hard to
formulate just like C++ in general.

>From measurements that I made it seems that adding one generalized
constructor and one const rejection constructor to the current auto_ptr will
eliminate its limitations. This means that auto_ptr_ref can stay in case
that someone explicitly use (read abuse) it and backward compatibility is
really needed. I think that it's not the case.
Nevertheless the member "template<class Y> operator auto_ptr<Y>()" must be
removed because it doesn't fulfill its original purpose, which is copy
initialization base from r-value derived that requires 3 (!) user defined
conversions, after DR #84 and adds unexpected ambiguity in the case of copy
initialization base from l-value derived. I'll specify this in the DR.

I think that if possible then auto_ptr_ref should be abandon mainly because
of the following opinion by the top C++ core language expert Steve Adamcyzk
in DR 84:
"To switch to personal opinion for a second, I think it's bad enough that
auto_ptr has to exploit a really *arcane loophole* of overload resolution,
but in this case it seems like it's exploiting a loophole on a loophole."

I hope that my suggestion doesn't exploit any arcane loophole. It basically
forces auto_ptr into accepting, via constructors, only non-const auto_ptr
which is derived from the fundamental transfer ownership requirement of
auto_ptr.

Thanks,
Rani

Rani Sharoni

unread,
Dec 3, 2003, 12:34:09 PM12/3/03
to
"Dhruv" wrote:
> I cannot understand a few things here.
I'll try to clearly them.

> I have a difficulty in understanding how an r-value can be non-const,
> because I understand r-values as values that occur on the right hand
> side of expressions, and whwch cannot be assigned to, meanain that
> they cannot act as l-values, or it it that every l-value is also an
> r-value but not the other way around?

3.10/1: Every expression is either an l-value or an r-value.
r-value is an expression that its address can't be taken using the
*built-in* address operator (in the spirit of 5.2.10/10). You can also think
about r-value as a result of function call that does not return reference
(3.10/5). *class* r-values can have cv-qualified types per 3.10/9 and you
can actually "abuse" this property to detected non-class types. This means
that, for example, you can refer/call to non const member of non-const
r-value. Known technique to "clear" containers involves non-const r-value -
vertor<int>().swap(my_vector).

Unfortunately non-const class r-values can't be bound to non-const reference
(per 8.5.3/5) which lead to heroic efforts to workaround this limitation.
There is an interesting proposal to (also) address this problem and more
generally allow move semantics support in C++:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

Excellent (read deep) lesson about this issue can be found at:
http://tinyurl.com/xin0 (auto_ptr update by Scoot Meyers).

> Also, I have difficulty in understand when the const qualifier in the
> function parameter stands for 1. The parameter will not be modified
> in the function body, and when it stands for 2. I will take only
> conatant values as my parameters?

The generalized "copy" constructor takes const reference that can be bound
to an arguments which includes non-consts. The private constructor template
generates ill-formed declaration when trying to construct auto_ptr using
const auto_ptr<U> for any U and doesn't exist for other types. This means
that only non-const auto_ptr are accepted which is the basic requirement.
Removing const qualifier from type that refers to modifiable object and
modifying it afterward is allowed.

Rani

David Abrahams

unread,
Dec 4, 2003, 7:09:05 PM12/4/03
to
rani_s...@hotmail.com ("Rani Sharoni") writes:

> David Abrahams wrote:
>> rani_s...@hotmail.com ("Rani Sharoni") writes:
>>
>>> Final (hopefully) suggestion, comments are more than welcome:
>>
>> That's wonderful! I sure hope you do submit a paper to the
>> committee. Actually, I hope you submit a DR which says that auto_ptr
>> is overspecified, suggesting wording which allows this implementation.
>>
>> Then I also challenge you to find a way to use this technique in
>> implementing an auto_ptr which is arguably standard-conforming, even
>> if the DR is not accepted. In other words, write an auto_ptr which
>> conforms to both the letter and the spirit of the standard :)
>
> I appreciate your feedback.
>
> I understand your concern

>From what you wrote below, I don't think you do. My concerns are

#1: I'd like implementations to be able to replace their auto_ptr
implementation with the superior one you posted as soon as
possible, i.e. before C++0x. The DR which says auto_ptr is
overspecified would accomplish that.

#2: In case we can't get that accepted as a DR, I'd like a
fallback which allows implementations to use your technique
to enable the currently ambiguous

auto_ptr<B> bp2 = dp;

case without removing auto_ptr_ref and without any change to
the standard text.


> but in fact my main motivation was to keep the spirit of the
> standard auto_ptr and come with an implementation that met *all* the
> requirements which motivated by intuitive usage yet hard to
> formulate just like C++ in general.
>
>>From measurements that I made it seems that adding one generalized
> constructor and one const rejection constructor to the current auto_ptr will
> eliminate its limitations. This means that auto_ptr_ref can stay in case
> that someone explicitly use (read abuse) it and backward compatibility is
> really needed.

Great! It sounds like you're saying you've done #2.

> I think that it's not the case.

...that backward compatibility is really needed?

> Nevertheless the member "template<class Y> operator auto_ptr<Y>()"
> must be removed because it doesn't fulfill its original purpose,
> which is copy initialization base from r-value derived that requires
> 3 (!) user defined conversions, after DR #84 and adds unexpected
> ambiguity in the case of copy initialization base from l-value
> derived. I'll specify this in the DR.

Yes, it must be removed; the question is: can we remove it now? I
think any DR which attempts to mandate that *implementations* remove
auto_ptr_ref is doomed to be postponed for C++0x because it would
render currently-conforming implementations broken. Merely saying
that auto_ptr is overspecified would allow us to remove the standard
language which requires auto_ptr_ref, without breaking
currently-working libraries.

> I think that if possible then auto_ptr_ref should be abandon mainly because
> of the following opinion by the top C++ core language expert Steve Adamcyzk
> in DR 84:
> "To switch to personal opinion for a second, I think it's bad enough that
> auto_ptr has to exploit a really *arcane loophole* of overload resolution,
> but in this case it seems like it's exploiting a loophole on a loophole."
>
> I hope that my suggestion doesn't exploit any arcane loophole. It basically
> forces auto_ptr into accepting, via constructors, only non-const auto_ptr
> which is derived from the fundamental transfer ownership requirement of
> auto_ptr.

Ya done good, kid.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

Rani Sharoni

unread,
Dec 5, 2003, 10:31:59 AM12/5/03
to
David Abrahams wrote:
> rani_s...@hotmail.com ("Rani Sharoni") writes:
>> I understand your concern
> From what you wrote below, I don't think you do. My concerns are
>
> #1: I'd like implementations to be able to replace their auto_ptr
> implementation with the superior one you posted as soon as
> possible, i.e. before C++0x. The DR which says auto_ptr is
> overspecified would accomplish that.
>
> #2: In case we can't get that accepted as a DR, I'd like a
> fallback which allows implementations to use your technique
> to enable the currently ambiguous
>
> auto_ptr<B> bp2 = dp;
>
> case without removing auto_ptr_ref and without any change to
> the standard text.
My proposal comes to serve the original intentions of the auto_ptr creators
and if the DR will not be accepted then the proposal is probably not good
enough and should not be used by any implementation.

The fact that the auto_ptr<Y> conversion operator doesn't serves its
original purpose and just adds ambiguity to one of the legal cases which
means that its contribution is merely negative and this serious defect in
auto_ptr. No one actually used this member in legal code unless she
explicitly wrote:
auto_ptr<B> bp = dp.operator auto_ptr<B>(); // static_cast will not help!

In this case I'll be happy to break this abuse especially since this spoiler
member also breaks my proposal with the same annoying subtle ambiguity.

I was surprised to find out that removing this zombie member will not help
the case I mentioned (without my fix):
auto_ptr<B> bp = dp; // EDG, GCC and VC - no suitable copy constructor

This copy initialization semantically requires copy constructor which means
that the both generalized conversion constructor and the auto_ptr_ref
conversion member are required which is what was explicitly forbidden in DR
#84. This is very amusing, removing the ambiguity between conversion
constructor and the zombie conversion member (both UDC in copy
initialization context) results with no candidate constructors for
initialization. Gosh, this simple facility bares some of the deepest secrets
of C++.

Abusing auto_ptr_ref in the same manner is also something that I don't care
to break. This is mainly because the standard originally specified it as
*private* member (read implementation detail) of auto_ptr which means that
the intention was to implicitly use it just for the special conversions.
While ago you noticed that auto_ptr_ref can't do any good as auto_ptr member
and TC DR #127 moved it to the namespace scope (I remember that year ago I
also noticed it and went into google groups search to find out that you
noticed it long before I did). I'm certain that the intention of DR #127 was
*not* to expose auto_ptr_ref for client usage while making the special
conversions viable.
Another issue that was probably overlooked is auto_ptr_ref exception safety
issue:

int f(auto_ptr<B>, std::string);
auto_ptr<B> source();

int x = f(source(), std::string("xyz"));

I'm sure that you, my exception safety mentor, will immediately understand
the problem and not be surprise to hear that it actually exists in all the
STL implementations (although compilers probably generates safe code). This
is not the same case that Herb Sutter discussed about in gotw and his book
(i.e. f(auto_ptr<B>(new D), std::string)).

This is why I think that removing auto_ptr_ref is not a big deal but since I
trust your judgment I copy paste the standard + DR #127 and added the extra,
r-value and const rejection, constructors. Changing the generalized
assignment operator to accept its parameter by-value will make auto_ptr_ref
zombie since the new constructor is better candidate in the relevant cases
and makes auto_ptr_ref obsolete (making its relevant members private is one
why to verify that) which will probably stratify you.

> Great! It sounds like you're saying you've done #2.

namespace std
{
template <class Y>
struct auto_ptr_ref { /* ... */ };

template<class X>
class auto_ptr
{
public:
typedef X element_type;

// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
// [Rani] add const


// non-const auto_ptr r-value (any U)

// const auto_ptr will result ill-formed instance

template<class Y> auto_ptr(auto_ptr<Y> const&) throw();
auto_ptr& operator=(auto_ptr&) throw();
// [Rani] by-value to allow r-value RHS
template<class Y> auto_ptr& operator=(auto_ptr<Y>) throw();


~auto_ptr() throw();
// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p =0) throw();

// 20.4.5.3 conversions:
// [Rani] private:
auto_ptr(auto_ptr_ref<X>) throw();
auto_ptr& operator=(auto_ptr_ref<X>) throw();
template<class Y> operator auto_ptr_ref<Y>() throw();
// [Rani] spoiler removed!
// template<class Y> operator auto_ptr<Y>() throw();

private:


//
// reject const auto_ptr's with compilation error

// during instantiation of decleration (14.7.1/1)


// other types will trigger SFINAE (14.8.2/2/3/3)
//

template<typename T> struct error_const_auto_ptr;

template<typename T> struct error_const_auto_ptr< auto_ptr<T> const
>
{ typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed
type; };

template<class U>


auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
};

} // namespace std

Dhruv

unread,
Dec 5, 2003, 4:54:48 PM12/5/03
to
On Wed, 03 Dec 2003 17:34:09 +0000, Rani Sharoni wrote:

> "Dhruv" wrote:
>> I cannot understand a few things here.
> I'll try to clearly them.
>
>> I have a difficulty in understanding how an r-value can be non-const,
>> because I understand r-values as values that occur on the right hand
>> side of expressions, and whwch cannot be assigned to, meanain that
>> they cannot act as l-values, or it it that every l-value is also an
>> r-value but not the other way around?
>
> 3.10/1: Every expression is either an l-value or an r-value.
> r-value is an expression that its address can't be taken using the
> *built-in* address operator (in the spirit of 5.2.10/10). You can also think
> about r-value as a result of function call that does not return reference
> (3.10/5). *class* r-values can have cv-qualified types per 3.10/9 and you
> can actually "abuse" this property to detected non-class types. This means
> that, for example, you can refer/call to non const member of non-const
> r-value. Known technique to "clear" containers involves non-const r-value -
> vertor<int>().swap(my_vector).
>
> Unfortunately non-const class r-values can't be bound to non-const reference
> (per 8.5.3/5) which lead to heroic efforts to workaround this limitation.
> There is an interesting proposal to (also) address this problem and more
> generally allow move semantics support in C++:
> http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm
>

Thanks a lot for the explanation!!!


Ok, so as I understand it, here it goes:

int foo ();
const int boo ();

Now, when passing the return of foo to a function like the copy ctor of
your auto_ptr implementation, the return value will be detected as being a
non-const value (r/l-value), so the const_cast, and stuff, while if I pass
the return of boo to a similar function, then the specialization that you
have made will be selected (for const objects), and then the instantiation
will fail... Amazing!!!

I was always thinking that r-values have to be const, because I could not
pass the result of functions to std::swap, which took non-const
references, so that's where my misunderstandng apose form.

> Excellent (read deep) lesson about this issue can be found at:
> http://tinyurl.com/xin0 (auto_ptr update by Scoot Meyers).
>
>> Also, I have difficulty in understand when the const qualifier in the
>> function parameter stands for 1. The parameter will not be modified
>> in the function body, and when it stands for 2. I will take only
>> conatant values as my parameters?
>
> The generalized "copy" constructor takes const reference that can be bound
> to an arguments which includes non-consts. The private constructor template
> generates ill-formed declaration when trying to construct auto_ptr using
> const auto_ptr<U> for any U and doesn't exist for other types. This means
> that only non-const auto_ptr are accepted which is the basic requirement.
> Removing const qualifier from type that refers to modifiable object and
> modifying it afterward is allowed.
>

Ok, I got this part...

However, one of my questions is still unanswered...

About detecting the type of parameters. Let me rephrase it to make myself
clearer: When will the const qualifier in the parameters of a function
declaration/definition take part in function overloading, and when will it
not???

I just can't seem to get this...


Regards,
-Dhruv.

Pete Becker

unread,
Dec 5, 2003, 4:54:53 PM12/5/03
to
David Abrahams wrote:
>
> #1: I'd like implementations to be able to replace their auto_ptr
> implementation with the superior one you posted as soon as
> possible, i.e. before C++0x. The DR which says auto_ptr is
> overspecified would accomplish that.
>
> #2: In case we can't get that accepted as a DR,

It shouldn't be accepted, because it isn't a defect. The standard says
exactly what we intended it to say, and auto_ptr works in the way it was
intended to work.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

David Abrahams

unread,
Dec 5, 2003, 4:55:25 PM12/5/03
to
rani_s...@hotmail.com ("Rani Sharoni") writes:

> Another issue that was probably overlooked is auto_ptr_ref exception safety
> issue:
>
> int f(auto_ptr<B>, std::string);
> auto_ptr<B> source();
>
> int x = f(source(), std::string("xyz"));
>
> I'm sure that you, my exception safety mentor, will immediately understand
> the problem and not be surprise to hear that it actually exists in all the
> STL implementations (although compilers probably generates safe code).

Whoops. Yep you're right. This one is nasty. The compiler can
reorganize things so that the string is constructed while only the
auto_ptr_ref has a hold on the pointer. DR, DR, DR :(.

I think this one is might even be serious enough to get the LWG to
consider adopting your rewrite of std::auto_ptr immediately. If you
can't do that with std::auto_ptr it fails to fulfill its promise in
so many ways...

Another problem I can see at the moment is that there's no
specification for auto_ptr_ref's copy ctor which says it can't throw.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

David Abrahams

unread,
Dec 5, 2003, 9:54:01 PM12/5/03
to
peteb...@acm.org (Pete Becker) writes:

> David Abrahams wrote:
>>
>> #1: I'd like implementations to be able to replace their auto_ptr
>> implementation with the superior one you posted as soon as
>> possible, i.e. before C++0x. The DR which says auto_ptr is
>> overspecified would accomplish that.
>>
>> #2: In case we can't get that accepted as a DR,
>
> It shouldn't be accepted, because it isn't a defect. The standard says
> exactly what we intended it to say, and auto_ptr works in the way it was
> intended to work.

No, it famously fails to do that. It was definitely intended that
this should compile:

#include <memory>
using namespace std;

struct B {};
struct D : B {};

auto_ptr<D> source();
auto_ptr<B> b = source();

Also, nobody anticipated or intended that when auto_ptr_ref was added
in the name of protecting newbies from using them as though copies
were equivalent, a much subtler and deadlier exeception saftey problem
was introduced in its place.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

Rani Sharoni

unread,
Dec 6, 2003, 2:54:55 PM12/6/03
to
Pete Becker wrote:
> David Abrahams wrote:
>>
>> #1: I'd like implementations to be able to replace their
>> auto_ptr implementation with the superior one you posted as
>> soon as possible, i.e. before C++0x. The DR which says
>> auto_ptr is overspecified would accomplish that.
>>
>> #2: In case we can't get that accepted as a DR,
>
> It shouldn't be accepted, because it isn't a defect. The standard says
> exactly what we intended it to say, and auto_ptr works in the way it
> was intended to work.

Thanks for the amazing insight of what happening behind the scene. Your
authorized statement merely tells the story of this misunderstood facility.
Believe me that I'm saying that due to my high appreciation for you deep
knowledge and expertise.

I submit the DR by the end of today.

Thanks,
Rani

BTW - I think that you should revise the current (i.e. based on the library
that was shipped with VC 2003) Dinkumware auto_ptr's implementation without
any relation to my proposal.

Pete Becker

unread,
Dec 6, 2003, 6:16:22 PM12/6/03
to
David Abrahams wrote:
>
> peteb...@acm.org (Pete Becker) writes:
>
> > David Abrahams wrote:
> >>
> >> #1: I'd like implementations to be able to replace their auto_ptr
> >> implementation with the superior one you posted as soon as
> >> possible, i.e. before C++0x. The DR which says auto_ptr is
> >> overspecified would accomplish that.
> >>
> > It shouldn't be accepted, because it isn't a defect. The standard says
> > exactly what we intended it to say, and auto_ptr works in the way it was
> > intended to work.
>
> No, it famously fails to do that. It was definitely intended that
> this should compile:
>

That's not "auto_ptr is overspecified."

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---

P.J. Plauger

unread,
Dec 6, 2003, 6:16:28 PM12/6/03
to
""Rani Sharoni"" <rani_s...@hotmail.com> wrote in message
news:3fd1...@news.microsoft.com...

> Pete Becker wrote:
> > David Abrahams wrote:
> >>
> >> #1: I'd like implementations to be able to replace their
> >> auto_ptr implementation with the superior one you posted as
> >> soon as possible, i.e. before C++0x. The DR which says
> >> auto_ptr is overspecified would accomplish that.
> >>
> >> #2: In case we can't get that accepted as a DR,
> >
> > It shouldn't be accepted, because it isn't a defect. The standard says
> > exactly what we intended it to say, and auto_ptr works in the way it
> > was intended to work.
>
> Thanks for the amazing insight of what happening behind the scene. Your
> authorized statement merely tells the story of this misunderstood
facility.
> Believe me that I'm saying that due to my high appreciation for you deep
> knowledge and expertise.
>
> I submit the DR by the end of today.

DRs are certainly welcome, particularly for auto_ptr. But given that this
template class was admittedly a compromise among several inconsistent
usages, it's not clear how to "fix" it.

> Thanks,
> Rani
>
> BTW - I think that you should revise the current (i.e. based on the
library
> that was shipped with VC 2003) Dinkumware auto_ptr's implementation
without
> any relation to my proposal.

We mostly do what compilers support. auto_ptr was designed to exploit
a loophole in the C++ Standard *specification*, which bore little
resemblance to C++ *implementations*. As the implementations converge,
so will the behavior of auto_ptr in various packagings of our library.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com

Francis Glassborow

unread,
Dec 6, 2003, 8:35:44 PM12/6/03
to
In article <uwu9ag...@boost-consulting.com>, David Abrahams
<da...@boost-consulting.com> writes

>Also, nobody anticipated or intended that when auto_ptr_ref was added
>in the name of protecting newbies from using them as though copies
>were equivalent, a much subtler and deadlier exeception saftey problem
>was introduced in its place.

As the major motive for auto_ptr was exception safety, a failure by it
to provide exception safety would, IMO, very definitely be a defect. It
does not achieve one of its design objectives. Subtle rarely seen
problems are the deadliest of all because even the best can be
completely unaware of them (like its taken five years for this one to
reach the collective consciousness of this group.


--
Francis Glassborow ACCU
If you are not using up-to-date virus protection you should not be reading
this. Viruses do not just hurt the infected but the whole community.

Rani Sharoni

unread,
Dec 7, 2003, 4:53:05 PM12/7/03
to
"Dhruv" wrote:
> Thanks a lot for the explanation!!!
>
> Ok, so as I understand it, here it goes:
>
> int foo ();
> const int boo ();
>
> Now, when passing the return of foo to a function like the copy ctor
> of
> your auto_ptr implementation, the return value will be detected as
> being a non-const value (r/l-value), so the const_cast, and stuff,
> while if I pass the return of boo to a similar function, then the
> specialization that you have made will be selected (for const
> objects), and then the instantiation will fail... Amazing!!!

I think that you got it. There is minor problem with your example since
non-class r-values always cv-unqualified types, as I mentioned before, per
3.10/9.

>> Removing const qualifier
>> from type that refers to modifiable object and modifying it
>> afterward is allowed.

Per 7.1.5.1/3 :)

> Ok, I got this part...
>
> However, one of my questions is still unanswered...
>
> About detecting the type of parameters. Let me rephrase it to make
> myself clearer: When will the const qualifier in the parameters of a
> function declaration/definition take part in function overloading,
> and when will it not???

I'm not sure that I understand you question. Can you elaborate with an
example?
In general const qualifier might affect the rank/viability of the function
during overloading.
OTOH void f(int) and void f(const int) are declarations of the same function
per 8.3.5/3.

Rani

Rani Sharoni

unread,
Dec 7, 2003, 4:53:05 PM12/7/03
to
"P.J. Plauger" wrote:
> DRs are certainly welcome, particularly for auto_ptr. But given that
> this template class was admittedly a compromise among several
> inconsistent usages, it's not clear how to "fix" it.

I certainly trust your authorized inside knowledge on core and library
issues but judging from the final auto_ptr proposal (analysis of conversion
operations http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1997/N1128.pdf)
it seems that Bill Gibbons and Greg Colvin thought that they overcome all
the usability issues and didn't compromise on consistent usage of this
facility.

I humble think that my proposal met all the intuitive auto_ptr requirements
(without exploiting any loophole) and therefore "fix" it. I'll be happy to
see cases in which my proposed auto_ptr has inconstant usage.

>> BTW - I think that you should revise the current (i.e. based on the
>> library that was shipped with VC 2003) Dinkumware auto_ptr's
>> implementation without any relation to my proposal.
>
> We mostly do what compilers support. auto_ptr was designed to exploit
> a loophole in the C++ Standard *specification*, which bore little
> resemblance to C++ *implementations*. As the implementations converge,
> so will the behavior of auto_ptr in various packagings of our library.

I didn't actually thought about compilers issues especially since VC7.1/8
has state of the art C++ font end and can handle auto_ptr as good as EDG and
GCC.

Try the following cases:
#include <memory>
using std::auto_ptr;

struct B {};
struct D : B {};

int main() {
auto_ptr<B> bp(auto_ptr<D>(0)); // #1
bp = auto_ptr<D>(); // #2
}

Case #2 is simple issue that related to DR #127.
Case #1 yield catastrophic generated code because 20.4.5/2 is *defective*.

Both are easy to fix.

Rani

Pete Becker

unread,
Dec 7, 2003, 4:53:29 PM12/7/03
to
Francis Glassborow wrote:
>
> In article <uwu9ag...@boost-consulting.com>, David Abrahams
> <da...@boost-consulting.com> writes
> >Also, nobody anticipated or intended that when auto_ptr_ref was added
> >in the name of protecting newbies from using them as though copies
> >were equivalent, a much subtler and deadlier exeception saftey problem
> >was introduced in its place.
>
> As the major motive for auto_ptr was exception safety, a failure by it
> to provide exception safety would, IMO, very definitely be a defect. It
> does not achieve one of its design objectives. Subtle rarely seen
> problems are the deadliest of all because even the best can be
> completely unaware of them (like its taken five years for this one to
> reach the collective consciousness of this group.
>

My point was that a defect report "which says auto_ptr is overspecified"
should be rejected. If there are other problems that people want to
address they should say so, and not use a bogus claim as a vehicle for
redesign.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---

David Abrahams

unread,
Dec 7, 2003, 8:03:49 PM12/7/03
to
peteb...@acm.org (Pete Becker) writes:

> David Abrahams wrote:
>>
>> peteb...@acm.org (Pete Becker) writes:
>>
>> > David Abrahams wrote:
>> >>
>> >> #1: I'd like implementations to be able to replace their auto_ptr
>> >> implementation with the superior one you posted as soon as
>> >> possible, i.e. before C++0x. The DR which says auto_ptr is
>> >> overspecified would accomplish that.
>> >>
>> > It shouldn't be accepted, because it isn't a defect. The standard says
>> > exactly what we intended it to say, and auto_ptr works in the way it was
>> > intended to work.
>>
>> No, it famously fails to do that. It was definitely intended that
>> this should compile:
>>
>
> That's not "auto_ptr is overspecified."

Right, it's just one consequence of overspecification. If we had only
written auto_ptr in terms of valid expressions, compilers and
libraries could've at least used "magic" to make it work as intended
until someone came up with Rani's solution. Instead, they're
constrained to do something which can't work as intended and has
unintended hidden dangers.

I still remember when David Vandevoorde was the lone man who stood up
in Morristown and said "this can't work". I didn't know what to think
at the time, but it seems like it gets truer every year. I think
we've done remarkably well overall: this is one of the very few but
notable areas where the C++ standard suffered due to political
neccessity.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

Matt Austern

unread,
Dec 7, 2003, 8:06:21 PM12/7/03
to
peteb...@acm.org (Pete Becker) writes:

> Francis Glassborow wrote:
> >
> > In article <uwu9ag...@boost-consulting.com>, David Abrahams
> > <da...@boost-consulting.com> writes
> > >Also, nobody anticipated or intended that when auto_ptr_ref was added
> > >in the name of protecting newbies from using them as though copies
> > >were equivalent, a much subtler and deadlier exeception saftey problem
> > >was introduced in its place.
> >
> > As the major motive for auto_ptr was exception safety, a failure by it
> > to provide exception safety would, IMO, very definitely be a defect. It
> > does not achieve one of its design objectives. Subtle rarely seen
> > problems are the deadliest of all because even the best can be
> > completely unaware of them (like its taken five years for this one to
> > reach the collective consciousness of this group.
> >
>
> My point was that a defect report "which says auto_ptr is overspecified"
> should be rejected. If there are other problems that people want to
> address they should say so, and not use a bogus claim as a vehicle for
> redesign.

I agree that giving the real problem we're trying to solve is always
good. On the other hand, considering that we're almost certainly
talking about changes for C++0x rather than changes for TC2, thinking
about redesign doesn't strike me as a bad idea.

Pete Becker

unread,
Dec 7, 2003, 8:50:01 PM12/7/03
to
David Abrahams wrote:
>
> peteb...@acm.org (Pete Becker) writes:
>
> > David Abrahams wrote:
> >>
> >> peteb...@acm.org (Pete Becker) writes:
> >>
> >> > David Abrahams wrote:
> >> >>
> >> >> #1: I'd like implementations to be able to replace their auto_ptr
> >> >> implementation with the superior one you posted as soon as
> >> >> possible, i.e. before C++0x. The DR which says auto_ptr is
> >> >> overspecified would accomplish that.
> >> >>
> >> > It shouldn't be accepted, because it isn't a defect. The standard says
> >> > exactly what we intended it to say, and auto_ptr works in the way it was
> >> > intended to work.
> >>
> >> No, it famously fails to do that. It was definitely intended that
> >> this should compile:
> >>
> >
> > That's not "auto_ptr is overspecified."
>
> Right, it's just one consequence of overspecification.

Huh? How does the claim that it doesn't work follow from the claimed
overspecification? If the specification is wrong, it's wrong. End of
discussion. Why play games with it? Just say what you mean.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---

Rani Sharoni

unread,
Dec 8, 2003, 10:06:19 AM12/8/03
to
Pete Becker wrote:
> David Abrahams wrote:
>> peteb...@acm.org (Pete Becker) writes:
>>> David Abrahams wrote:
>>>> peteb...@acm.org (Pete Becker) writes:
>>>>> David Abrahams wrote:
>>>>>>
>>>>>> #1: I'd like implementations to be able to replace their
>>>>>> auto_ptr implementation with the superior one you
>>>>>> posted as soon as possible, i.e. before C++0x. The DR
>>>>>> which says auto_ptr is overspecified would accomplish
>>>>>> that.
>>>>>>
>>>>> It shouldn't be accepted, because it isn't a defect. The standard
>>>>> says exactly what we intended it to say, and auto_ptr works in
>>>>> the way it was intended to work.
>>>>
>>>> No, it famously fails to do that. It was definitely intended that
>>>> this should compile:
>>> That's not "auto_ptr is overspecified."
>> Right, it's just one consequence of overspecification.
>
> Huh? How does the claim that it doesn't work follow from the claimed
> overspecification? If the specification is wrong, it's wrong. End of
> discussion. Why play games with it? Just say what you mean.

I, again, absolutely agree with Dave Abrahams although I admit that I didn't
immediately understand his insightful intention and only after formulating
the DR I think that I fully understand how right he was.

auto_ptr is an excellent example of over-specified facility. The auto_ptr
semantic requirements are well defined and once acceptable evidence that
those requirements are achievable it is legitimate specifying them, wisely,
in abstract fashion without mandating any specific implementation. Requiring
that auto_ptr can be constructed and be assigned only from non const
auto_ptr, which results from its strict and transfer ownership semantics, is
enough once accepted as being achievable. There is no need to constrain
library providers and library consumers with detailed underling
implementation mechanisms that meant to add nothing to its abstract
definition. I'm sure that the auto_ptr implementation techniques are quite
exciting but once they are also used to *define* such abstract facility an
important degree of freedom is lost and this freedom is one of the most
important properties of abstraction.

The standard actually contains good examples of abstract facilities and the
one that comes in mind is the standard iterator concept. "typedef
implementation defined iterator; // See 23.1" is wonderfully great design
decision. The current auto_ptr requirements are not far from requiring
std::vector iterator to be a pointer.
The strict implementation requirements of auto_ptr proved being tricky,
painful and even harmful and should be avoided.

Rani

Dhruv

unread,
Dec 8, 2003, 1:16:43 PM12/8/03
to
On Sun, 07 Dec 2003 21:53:05 +0000, Rani Sharoni wrote:

> "Dhruv" wrote:
>> Thanks a lot for the explanation!!!
>>
>> Ok, so as I understand it, here it goes:
>>
>> int foo ();
>> const int boo ();
>>
>> Now, when passing the return of foo to a function like the copy ctor
>> of
>> your auto_ptr implementation, the return value will be detected as
>> being a non-const value (r/l-value), so the const_cast, and stuff,
>> while if I pass the return of boo to a similar function, then the
>> specialization that you have made will be selected (for const
>> objects), and then the instantiation will fail... Amazing!!!
>
> I think that you got it. There is minor problem with your example since
> non-class r-values always cv-unqualified types, as I mentioned before, per
> 3.10/9.
>

Ok, so say you have:

int foo ();

Then the typeof (foo) would be const volatile int instead of plain int?


>>> Removing const qualifier
>>> from type that refers to modifiable object and modifying it
>>> afterward is allowed.
> Per 7.1.5.1/3 :)
>
>> Ok, I got this part...
>>
>> However, one of my questions is still unanswered...
>>
>> About detecting the type of parameters. Let me rephrase it to make
>> myself clearer: When will the const qualifier in the parameters of a
>> function declaration/definition take part in function overloading,
>> and when will it not???
>
> I'm not sure that I understand you question. Can you elaborate with an
> example?
> In general const qualifier might affect the rank/viability of the function
> during overloading.
> OTOH void f(int) and void f(const int) are declarations of the same function
> per 8.3.5/3.

Yes, this is exactly what I don't seem to get... Whne do the const
qualifications affect overloading and when do they do not?

Like in the example you gave they do not, and they just mean do not modify
me in the function, while:

void foo (int const& cri);
and:
void foo (int& ri);

seem to be overloadable for const and non-const r-values (I guess)?

Regards,
-Dhruv.

Pete Becker

unread,
Dec 9, 2003, 1:42:13 AM12/9/03
to
Rani Sharoni wrote:
>
> auto_ptr is an excellent example of over-specified facility. The auto_ptr
> semantic requirements are well defined and once acceptable evidence that
> those requirements are achievable it is legitimate specifying them, wisely,
> in abstract fashion without mandating any specific implementation. Requiring
> that auto_ptr can be constructed and be assigned only from non const
> auto_ptr, which results from its strict and transfer ownership semantics, is
> enough once accepted as being achievable. There is no need to constrain
> library providers and library consumers with detailed underling
> implementation mechanisms that meant to add nothing to its abstract
> definition. I'm sure that the auto_ptr implementation techniques are quite
> exciting but once they are also used to *define* such abstract facility an
> important degree of freedom is lost and this freedom is one of the most
> important properties of abstraction.

"It should have been written differently" is not a defect. The
specification for auto_ptr says what we meant it to say, and we intended
to restrict it to that particular form of implementation. If it doesn't
work, that's a defect. But "it's overspecified" isn't the problem, it's
a pointer to one possible solution. There may be others.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---

Rani Sharoni

unread,
Dec 9, 2003, 10:40:00 AM12/9/03
to
Dhruv wrote:
> On Sun, 07 Dec 2003 21:53:05 +0000, Rani Sharoni wrote:
>> I think that you got it. There is minor problem with your example
>> since non-class r-values always cv-unqualified types, as I mentioned
>> before, per 3.10/9.
> int foo ();
>
> Then the typeof (foo) would be const volatile int instead of plain int?

On the contrary, Read it more carefully: non-class r-values always
*cv-unqualified* types.
const int func();
func() is an r-value expression of type int (non const).

typeof(func) is a bit different since func is of function type:
template<typename T> int what_type(T&) {
typedef typename T::what type;
return 0;
}

int x = what_type(func); // error with side effect T=const int ()

Ask your favorite compiler what it thinks about it.

> Yes, this is exactly what I don't seem to get... Whne do the const
> qualifications affect overloading and when do they do not?
>
> Like in the example you gave they do not, and they just mean do not
> modify
> me in the function, while:
>

> void foo (int const& cri); // #1
> and:
> void foo (int& ri); // #2


>
> seem to be overloadable for const and non-const r-values (I guess)?

Both functions have exact match for non-const l-value but #2 is obviously
better (per 13.3.3.2/3) and #2 is not viable for r-values (per
13.3.3.1.4/3).

Overloading is a bit out of my league but maybe the standard can clarify
things:

13.3.3.2/3:
Two implicit conversion sequences of the same form are indistinguishable
conversion sequences unless one of the following rules apply:
- Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if
[...]
S1 and S2 are reference bindings (8.5.3), and the types to which the
references refer are the same type except for top-level cv-qualifiers, and
the type to which the reference initialized by S2 refers is more
cv-qualified than the type to which the reference initialized by S1 refers.
[Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // Calls f(int &)
int k = g(i); // ambiguous

class X {
public:
void f() const;
void f();
};

void g(const X& a, X b)
{
a.f(); //CallsX::f() const
b.f(); //Calls X::f()
}

Rani

James Kuyper

unread,
Dec 9, 2003, 12:35:57 PM12/9/03
to
dhru...@gmx.net (Dhruv) wrote in message news:<pan.2003.12.08....@gmx.net>...

> On Sun, 07 Dec 2003 21:53:05 +0000, Rani Sharoni wrote:
...

> > OTOH void f(int) and void f(const int) are declarations of the same function
> > per 8.3.5/3.
>
> Yes, this is exactly what I don't seem to get... Whne do the const
> qualifications affect overloading and when do they do not?
>
> Like in the example you gave they do not, and they just mean do not modify
> me in the function, while:
>
> void foo (int const& cri);
> and:
> void foo (int& ri);
>
> seem to be overloadable for const and non-const r-values (I guess)?

Take another look at 8.3.5p3, as he specified. It says that "... After
producing the list of parameter types, several transformations take
place upon these types to determine the function type. Any
_cv-qualifer_ modifying a parameter type is deleted. ...". Therefore,
your first declaration declares foo() to have the same function type
as the second declaration, and therefore declares the same function.

The 'const' has a meaning only when it occurs in the particular
declaration of a function that happens to start the definition of that
function - in that context, it makes the parameter 'const' within the
scope of that function, but it has no effect on the function's actual
type. For any other declaration of the function, the 'const' is
useless, and might as well be dropped.

Francis Glassborow

unread,
Dec 9, 2003, 12:54:05 PM12/9/03
to
In article <8b42afac.03120...@posting.google.com>, James
Kuyper <kuy...@wizard.net> writes

I think you have misunderstood. It is only the top level const (i.e. one
that could appear as the last token of a type name) that is discarded.
int const & and int & are very definitely different types and ones that
can be distinguished for the purposes of overloading.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit

David Abrahams

unread,
Dec 9, 2003, 9:47:22 PM12/9/03
to
peteb...@acm.org (Pete Becker) writes:

> Rani Sharoni wrote:
>>
>> auto_ptr is an excellent example of over-specified facility. The auto_ptr
>> semantic requirements are well defined and once acceptable evidence that
>> those requirements are achievable it is legitimate specifying them, wisely,
>> in abstract fashion without mandating any specific implementation. Requiring
>> that auto_ptr can be constructed and be assigned only from non const
>> auto_ptr, which results from its strict and transfer ownership semantics, is
>> enough once accepted as being achievable. There is no need to constrain
>> library providers and library consumers with detailed underling
>> implementation mechanisms that meant to add nothing to its abstract
>> definition. I'm sure that the auto_ptr implementation techniques are quite
>> exciting but once they are also used to *define* such abstract facility an
>> important degree of freedom is lost and this freedom is one of the most
>> important properties of abstraction.
>
> "It should have been written differently" is not a defect. The
> specification for auto_ptr says what we meant it to say, and we intended
> to restrict it to that particular form of implementation. If it doesn't
> work, that's a defect. But "it's overspecified" isn't the problem, it's
> a pointer to one possible solution. There may be others.

I think you make a good point in suggesting that if it's broken we
should just come out and say that. All the same, it's far from clear
to me that we intended to restrict it to that particular form of
implementation. IIRC those auto_ptr_ref changes were made under the
gun, arguably without even enough time for most of us to understand
why it worked (or didn't, as the case to be), much less any time for
consideration of how the wording would constrain implementations.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

Rani Sharoni

unread,
Dec 9, 2003, 10:42:28 PM12/9/03
to
Pete Becker wrote:
> Rani Sharoni wrote:
>>
>> auto_ptr is an excellent example of over-specified facility. The
>> auto_ptr semantic requirements are well defined and once acceptable
>> evidence that those requirements are achievable it is legitimate
>> specifying them, wisely, in abstract fashion without mandating any
>> specific implementation. Requiring that auto_ptr can be constructed
>> and be assigned only from non const auto_ptr, which results from its
>> strict and transfer ownership semantics, is enough once accepted as
>> being achievable. There is no need to constrain library providers
>> and library consumers with detailed underling implementation
>> mechanisms that meant to add nothing to its abstract definition. I'm
>> sure that the auto_ptr implementation techniques are quite exciting
>> but once they are also used to *define* such abstract facility an
>> important degree of freedom is lost and this freedom is one of the
>> most important properties of abstraction.
>
> "It should have been written differently" is not a defect. The
> specification for auto_ptr says what we meant it to say, and we
> intended to restrict it to that particular form of implementation. If it
> doesn't work, that's a defect. But "it's overspecified" isn't the problem,
> it's a pointer to one possible solution. There may be others.

You mentioned more than once that the specifications for auto_ptr says what
you meant it to say but, although I'm not authorized as you are, my
impression is that the specifications for auto_ptr were written in terms of
specific implementation and therefore only partially says what you meant it
to say and sometimes says unexpected things. I'm sure that you didn't intend
to forbid some of the initializations that were explicitly specified in the
final official proposal, add exception safety issues and infinite recursion
during some of the initializations.

Your argument also confuses me since I figured out (especially from DR #127)
that the original auto_ptr tricky parts were not fully tested on compliant
(in the relevant areas) C++ compiler. It seems that auto_ptr is theoretical
facility that proves the well known cliche "if it's not tested then it's
broken". IMHO this is direct result of the over-specified strict and very
complicated underling implementations requirements.

Rani

Francis Glassborow

unread,
Dec 10, 2003, 12:26:45 PM12/10/03
to
In article <uy8tlk...@boost-consulting.com>, David Abrahams
<da...@boost-consulting.com> writes

>I think you make a good point in suggesting that if it's broken we
>should just come out and say that. All the same, it's far from clear
>to me that we intended to restrict it to that particular form of
>implementation. IIRC those auto_ptr_ref changes were made under the
>gun, arguably without even enough time for most of us to understand
>why it worked (or didn't, as the case to be), much less any time for
>consideration of how the wording would constrain implementations.

Yep, the UK required it to be fixed or removed. If it has now been
discovered that it does not do what we intended then it wasn't fixed and
that is a defect. Unfortunately like many decisions in developing the
C++ Standard the exact intent has not been recorded only the final
decisions.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit

---

Dhruv

unread,
Dec 10, 2003, 12:26:46 PM12/10/03
to
On Mon, 01 Dec 2003 17:39:21 +0000, Rani Sharoni wrote:

I tried something similar with test classes, but I cann't seem to get the
same effect. I am obviously missing something. AFAIK, r-values would not
bind to copy ctors taking types by non-const reference (T&), but I can't
undersand how Rani has made them (const l/r-values) bind to such ctors???
Any reference to the standard would be appreciated.


template <bool>
struct Instantiate { };

template <>
struct Instantiate<false>;

template <typename T>
struct Foo {
Foo (Foo const&) { }
Foo (Foo&) { }
Foo () { }
};


template <typename T>
struct byref {

template <typename Type>
byref (const Type&)
{
Instantiate<true> i;
}

template <typename Type>
byref (Type&)
{
Instantiate<true> i;
}

};

Foo<double> const x () { return Foo<double>(); }

int main ()
{
byref<int> B (x());
}


And the warnings I get on compiling are:

type_test.cpp: In function `int main()':
type_test.cpp:68: warning: unused variable `byref<int> B'
type_test.cpp: In member function `byref<T>::byref(const Type&) [with Type =
Foo<double>, T = int]':
type_test.cpp:68: instantiated from here
type_test.cpp:43: warning: unused variable `Instantiate<true> i'


Suggesting that the ctor taking const references is preferred. So, how can
one make the non-const one get bound or such situations like how Rani has
done.


Regards,
-Dhruv.

Francis Glassborow

unread,
Dec 10, 2003, 1:22:30 PM12/10/03
to
In article <3fd5...@news.microsoft.com>, Rani Sharoni
<rani_s...@hotmail.com> writes

>Your argument also confuses me since I figured out (especially from DR #127)
>that the original auto_ptr tricky parts were not fully tested on compliant
>(in the relevant areas) C++ compiler. It seems that auto_ptr is theoretical
>facility that proves the well known cliche "if it's not tested then it's
>broken". IMHO this is direct result of the over-specified strict and very
>complicated underling implementations requirements.

I think I agree with you. I think we wrote into the Standard an example
of how our objectives might be met. As it happens that mechanism does
not actually deliver all it was promised to do. However I remember very
distinctly the proposer's saying that we should not worry over much
because by the time of the next C++ Standard the underlying core issues
would be fixed so auto_ptr would (easily) do what was required.

If your solution actually does work robustly (I am not saying it
doesn't) and meets the design specs we should be thankful and allow its
use ASAP. However I think we should give it an extensive shake-down. In
the meantime if we can modify the Standard to make solutions such as
yours conforming we should do so. I have not had time to look at this in
detail but reclassifying the auto_ptr definition as non-normative might
be enough if supplemented with an exact specification of what auto_ptr
should deliver.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit

---

Bronek Kozicki

unread,
Dec 10, 2003, 1:22:50 PM12/10/03
to
On Mon, 1 Dec 2003 17:39:21 +0000 (UTC), "Rani Sharoni" wrote:
> Again, Comments are more than welcome.

It's slightly off-topic here, but I want to point out one unpleasent
side effect of having conversion from auto_ptr<Derived> to
auto_ptr<Base>. Look here:

class B {};
class D : public B{};

void sink(auto_ptr<B>) {/* ... */}

int main()
{
auto_ptr<D> bp4 = auto_ptr<D>(new D);
sink(source());
}

We have undefined behaviour here per clause 5.3.5/3. Class B does not
have virtual destructor, thus wrong destructor will get "called" when
actual parameter of sink function goes out of scope.

I believe that after has_virtual_destructor (see document N1519[1]) has
been accepted into language, we have tool at hand to resolve this
problem. My suggestion is to make conversion from auto_ptr<Derived> to
auto_ptr<Base> illegal, if Base does not have virtual destructor. This
might be implemented only on these compiler which supports
has_virtual_destructor [2]. Example implementation would look similar
to:

template<class X>
class auto_ptr
{
public:
// ...
template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()
: ptr_(const_cast<auto_ptr<U>&>(rhs).release())
{
STATIC_ASSERT(!is_base_and_derived<X, U>::value ||
has_virtual_destructor<X>::value)
}

template<class Y>
auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{
STATIC_ASSERT(!is_base_and_derived<X, Y>::value ||
has_virtual_destructor<X>::value)

reset(rhs.release());
return *this;
}


Without this change of auto_ptr specification we have auto_ptr which
(under some conditions) does not add exception safety, and introduces
undefined behaviour instead. There are two strong points against making
this change:
1. some programs will not compile any more
2. some compilers will not support it

Ad 1. these programs are experiencing undefined behaviour anyway, or
will experience it in face of exceptions. If intent of programmer was
NOT to pass ownership of pointed object, he should have used raw pointer
or shared_ptr instead of auto_ptr. If intent of programmer was to pass
ownership, he should have made virtual destructor of base class or use
shared_ptr.

Ad 2. not all compilers will support has_virtual_destructor. Committee
has defined fallback option when compiler does not support it, ie.
always return false from "has_virtual_destructor<X>::value" . This would
render all conversions from auto_ptr<Derived> to auto_ptr<Base> illegal
(per suggested above STATIC_ASSERT). More reasonable fallback is
necessary: I suggest to make all conversions from auto_ptr<Derived> to
auto_ptr<Base> legal. Unfortunatelly it's incosistent with Committee
solution, but I see no better way. Improved STATIC_ASSERT would look
similar to:
STATIC_ASSERT(UNSUPPORTED_HAS_VIRTUAL_DESTRUCTOR ||
!is_base_and_derived<X, U>::value || has_virtual_destructor<X>::value)
It actually means "do not implement proposed change of auto_ptr, if
compiler does not support has_virtual_destructor".

There is another possible way of resolving undefined behaviour problem -
store static information on dynamic type of pointed object, as done in
shared_ptr [4]. I do not think that it's viable option in auto_ptr,
because of performance cost (additional allocation from heap). But maybe
someone can come up with similar (and cheap) alternative ? And the last
word: shared_ptr is superior to auto_ptr, but I do believe that there is
place for both in the C++ standard library.


B.


[1] http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1519.htm .There
is non-working link [3] to N1508, which contains discussion on
has_virtual_destructor and is_safely_destructible - later has not been
accepted by Committee, but I think it's not needed if above proposed
change of auto_ptr will be acceppted.

[2] currently none, but there is fallback option not to support this
change of auto_ptr, as discussed above.

[3] correct link is
http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1508.htm

[4] http://www.boost.org/libs/smart_ptr/shared_ptr.htm and
http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1450.html

Rani Sharoni

unread,
Dec 10, 2003, 1:23:38 PM12/10/03
to
"Dhruv" wrote:
> Suggesting that the ctor taking const references is preferred. So,
> how can one make the non-const one get bound or such situations like
> how Rani has done.

I have to admit that I did something simple relative to what you're asking.
I only brutally kicked away the const initialization arguments and you want
to *distinguish* between the cases. I wish that I was smart enough to do
invent such technique in the same manner that I did for the proposed
auto_ptr. Well, the smart creators of the current auto_ptr actually did
major step toward that goal probably without knowing it. Years later Andrei
Alexandrescu discovered (also using wonderful tip by Dave Abrahams) the
potential of the auto_ptr technique and accomplished what you asked for
(perhaps with different goals):
http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/alexandr.htm

Much more far reaching goals where targeted in the following proposal to add
move semantics support to C++:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

Enjoy,
Rani

Bronek Kozicki

unread,
Dec 11, 2003, 1:53:40 AM12/11/03
to
On Wed, 10 Dec 2003 18:22:50 +0000 (UTC), Bronek Kozicki wrote:
> class B {};
> class D : public B{};
>
> void sink(auto_ptr<B>) {/* ... */}
>
> int main()
> {
> auto_ptr<D> bp4 = auto_ptr<D>(new D);
> sink(source());

Obviously, the above line should read:
sink(bp4);

> }
>
> We have undefined behaviour here per clause 5.3.5/3. Class B does not
> have virtual destructor, thus wrong destructor will get "called" when
> actual parameter of sink function goes out of scope.

[...]


> There is another possible way of resolving undefined behaviour problem -
> store static information on dynamic type of pointed object, as done in
> shared_ptr [4]. I do not think that it's viable option in auto_ptr,
> because of performance cost (additional allocation from heap). But maybe
> someone can come up with similar (and cheap) alternative ? And the last

I really was not expecting that there is solution for this problem, but
now I can see one, and I hope it's good enough. At least it's nice and
simple. We just add one static function to auto_ptr:

template<class X>
class auto_ptr
{

private:
static void deleter(void * pv)
{
X* px = reinterpret_cast<X*> (pv);
delete px;
}

and store pointer to this function together with our managed pointer:

typedef void (* deleter_t) (void *);

X* ptr_;
deleter_t del_;

Pointer del_ has to be initialized when auto_ptr is being constructed
from raw pointer (thus storing static type of pointed object), and
passed around when auto_ptr is being copied:

template<class Y> friend class auto_ptr;

public:
explicit auto_ptr(X* p = 0) throw()
: ptr_(p), del_(&auto_ptr<X>::deleter)
{}

auto_ptr(auto_ptr& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()), del_(rhs.del_)
{}

template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()

: ptr_(const_cast<auto_ptr<U>&>(rhs).release()), del_(rhs.del_)
{}

auto_ptr& operator=(auto_ptr& rhs) throw()
{
reset(rhs.release());
del_ = rhs.del_;
return *this;
}

template<class Y>
auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{

reset(rhs.release());
del_ = rhs.del_;
return *this;
}

Instead of calling "delete" on managed pointer, we call pointed
"deleter" function:

void reset(X* p = 0) throw()
{
if (ptr_ != p)
{
del_(get());
ptr_ = p;
}
}

That's it. Cost of this solution is really small: one additional pointer
stored and copied together with managed pointer, one more function
instantiation and one more function call (or rather "redirection") at
deletion. Simple solution, with no heap allocation, no virtual functions
or anything else which might be expensive at runtime. Actually, del_
pointer is emulating pointer to virtual function acting as destructor
call. I hope that Committee will agree with me that cost of storing and
copying additional pointer is negligible.

Besides security we gain something extra: user can define specialization
of "deleter" function, providing customized deletion for classes with
unusual deletion semantics.

There are still two cases, when this solution is not protecting against
stated undefined behaviour problem:
1. when auto_ptr<Base> is being initialized with raw pointer Derived*.
2. when auto_ptr<Base> is being initialized with raw pointer Base*, but
dynamic type of pointed object is Derived.

While I believe that there is simple solution for 1., there's no good
solution for 2. I suggest to consider these cases as clearly requiring
virtual destructors and leave it as it is. Or maybe someone will bring
solution for 1. ?

I modified Rani's final proposal to implement proposed changes. I also
modified test cases. Here is complete code:


#include <cassert>
#include <cstdio>

// Proposed auto_ptr implementation start

template<class X>
class auto_ptr
{

static void deleter(void * pv)
{
X* px = reinterpret_cast<X*> (pv);
delete px;
}

typedef void (* deleter_t) (void *);

public:
typedef X element_type;

explicit auto_ptr(X* p = 0) throw()
: ptr_(p), del_(&auto_ptr<X>::deleter)
{}

#ifndef _MSC_VER
auto_ptr(auto_ptr& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()), del_(rhs.del_)
{}
#else
auto_ptr(auto_ptr const& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()), del_(rhs.del_)
{}
#endif


template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()

: ptr_(const_cast<auto_ptr<U>&>(rhs).release()), del_(rhs.del_)
{}

auto_ptr& operator=(auto_ptr& rhs) throw()
{
reset(rhs.release());
del_ = rhs.del_;
return *this;
}

template<class Y>
auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{

reset(rhs.release());
del_ = rhs.del_;
return *this;
}

~auto_ptr() throw()
{
reset();
}

// 20.4.5.2 members:
X& operator* () const throw()
{
assert(ptr_);
return *get();
}

X* operator->() const throw()
{
return &*(*this);
}

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

X* release() throw()
{
X* ptr = get();
ptr_ = 0;
return ptr;
}

void reset(X* p = 0) throw()
{
if (ptr_ != p)
{
del_(get());
ptr_ = p;
}
}

private:
X* ptr_;
deleter_t del_;

private:
template<class Y> friend class auto_ptr;

template<typename T> struct error_const_auto_ptr;

template<typename T> struct error_const_auto_ptr< auto_ptr<T> const >
{ typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };

template<class U>
auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
};

// Proposed auto_ptr implementation end

// Test cases

struct B
{
B()
{
printf("B::B\t%p\n", this);
}
~B()
{
printf("B::~B\t%p\n\n", this);
}
};

// Class B has no virtual destructor, thus undefined behaviour
// will occur when object of class F or D is being deleted through
// pointer to B. On some compilers this undefined behaviour
// is to simply execute wrong destructor, which
// allows us to trace lifetime of our test objects.

struct D : B
{
D()
{
printf("D::D\t%p\n\n", this);
}
~D()
{
printf("D::~D\t%p\n", this);
}
};

// Class F has unusual deletion semantics
struct F : B
{
F()
{
printf("F::F\t%p\n\n", this);
}
void free() {delete this;}

private:
~F()
{
printf("F::~F\t%p\n", this);
}
};

// Specialized deleter for class F
template<>
void auto_ptr<F>::deleter(void *pv)
{
F* px = reinterpret_cast<F*> (pv);
printf("freeing F\t%p\n", px);
px->free();
}

auto_ptr<D> source()
{
return auto_ptr<D>(new D);
}

void sink(auto_ptr<B>) {}

int main()
{
{
// receive pointer to auto_ptr<D>
// and store it in auto_ptr<B>
auto_ptr<B> b1 = source();
}

{
// create auto_ptr<D> and pass it
// to function taking auto_ptr<B>
auto_ptr<D> d1 = auto_ptr<D>(new D);
sink(d1);
}

{
// create pointer auto_ptr<F> to and
// store it in auto_ptr<B> . Class F has
// unusual deletion semantics
auto_ptr<B> b2 = auto_ptr<F>(new F);
}

{
// case 1. possibly could be fixed
auto_ptr<B> b3 = auto_ptr<B>(new D);
}

{
// case 2. I cannot imagine solution for it
B* b4raw = new D;
auto_ptr<B> b4 = auto_ptr<B>(b4raw);
}
}


Kind regards


B.

Dhruv

unread,
Dec 11, 2003, 7:51:46 PM12/11/03
to
On Wed, 10 Dec 2003 18:23:38 +0000, Rani Sharoni wrote:

> "Dhruv" wrote:
>> Suggesting that the ctor taking const references is preferred. So,
>> how can one make the non-const one get bound or such situations like
>> how Rani has done.
>
> I have to admit that I did something simple relative to what you're asking.
> I only brutally kicked away the const initialization arguments and you want
> to *distinguish* between the cases. I wish that I was smart enough to do
> invent such technique in the same manner that I did for the proposed
> auto_ptr.

Well, you have me completely lost here. I can't get what you are saying
apart form the fact that you have done something similar to what I'm
trying to do, and in a much more brutal way??? I tried out some more code.
This time, making another ctor taking a reference, and then that stuff
where 'type' is not defined for byref<T> const types, and it worked. Now
what I have gotten (undersood) upto now is that if you pass 'const foo'
l-values, and there is only one ctor taking U&, defined as such:

template <typename U>
byref <U&>;

Then the type of U would be deduced to be const foo, and if you pass foo
l-values, then U gets deduced to be foo. Fine. Now, suppose you have one
more ctor taking 'const U&' like you have in your auto_ptr class, then for
almost all calls (except for non-const l-values), the 2nd one will be
selected, so the ctor taking U& would never come into the picture for the
compiler right? So, How in the World did you manage to make the compiler
look at that function, and on top of it, make the compiler try to
instantiate it. The very fact that the compiler complains that the type
error_... is not defined means that it tried to specialize that function,
meaning that the compiler felt that it was a better match than const U&
???

Then, I tried doing something similar to what you have done, in trying to
make the compile fail for const objects, and it works, but I still don't
understand why the compiler even looks at the ctor taking U& for r-values
when const U& is there???


> Well, the smart creators of the current auto_ptr actually did
> major step toward that goal probably without knowing it. Years later Andrei
> Alexandrescu discovered (also using wonderful tip by Dave Abrahams) the
> potential of the auto_ptr technique and accomplished what you asked for
> (perhaps with different goals):
> http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/alexandr.htm
>

Thanks for the link ;-)

I got to learn quite a lot from that!!! Yes, the technique for selecting
temporaries is quite good, apart form the fact that non-const l-values are
left in the dark.


Regards,
-Dhruv.

Bronek Kozicki

unread,
Dec 11, 2003, 9:23:28 PM12/11/03
to
On Thu, 11 Dec 2003 00:53:40 CST, Bronek Kozicki wrote:
> There are still two cases, when this solution is not protecting against
> stated undefined behaviour problem:
> 1. when auto_ptr<Base> is being initialized with raw pointer Derived*.

fix for this issue is to add another template constructor:

template <class V>
explicit auto_ptr(V* p = 0) throw()
: ptr_(p), del_(&auto_ptr<V>::deleter)
{}

Dhruv

unread,
Dec 12, 2003, 2:50:49 PM12/12/03
to
On Fri, 05 Dec 2003 21:55:25 +0000, David Abrahams wrote:

> rani_s...@hotmail.com ("Rani Sharoni") writes:
>
>> Another issue that was probably overlooked is auto_ptr_ref exception safety
>> issue:
>>
>> int f(auto_ptr<B>, std::string);
>> auto_ptr<B> source();
>>
>> int x = f(source(), std::string("xyz"));
>>
>> I'm sure that you, my exception safety mentor, will immediately understand
>> the problem and not be surprise to hear that it actually exists in all the
>> STL implementations (although compilers probably generates safe code).
>
> Whoops. Yep you're right. This one is nasty. The compiler can
> reorganize things so that the string is constructed while only the
> auto_ptr_ref has a hold on the pointer. DR, DR, DR :(.
>

I'm no expert, but just as a thought, can we for the time being, remedy
the situation by delaying the transfer of ownership by not constructing
auto_ptr_ref with a pointer, but instead by a const reference to auto_ptr,
and store that in the data section of auto_ptr_ref as a reference, then we
can delay the transfer of ownership upto the time when it's time for the
destination to get the ownership. Then it's his responsibility to
destroy, or call release on the source, which is stored as a reference in
the auto_ptr_ref's data section. Something like this:

And have a forward declaration of auto_ptr.


//Auto_Ptr_Ref
template<typename _Tp1>
struct auto_ptr_ref
{
auto_ptr<_Tp1>& _M_ptr;

explicit
auto_ptr_ref(auto_ptr<_Tp1> const& __p)
: _M_ptr(const_cast<auto_ptr<_Tp1>& >(__p)) {}
//I guess that we will never get a const auto_ptr here, because we have
done enough to prevent that from happening.
};


//Inside auto_ptr

template<typename _Tp1>
operator auto_ptr_ref<_Tp1>() throw()
{
// return auto_ptr_ref<_Tp1>(this->release()); }
return auto_ptr_ref<_Tp1>(*this); }

auto_ptr(auto_ptr_ref<element_type> __ref) throw()
: _M_ptr(__ref._M_ptr) {
//Added the line below.
__ref._M_ptr.release ();
}

//And for the assignment operator.

I cannot see any obvious defects in the above, but I could always have
overlooked something. Please let me know if there is anything seriously
wrong, because I intend using this...


Regards,
-Dhruv.

Rani Sharoni

unread,
Dec 12, 2003, 2:51:21 PM12/12/03
to
""Dhruv"" <dhru...@gmx.net> wrote in message
news:pan.2003.12.11....@gmx.net...

> Then, I tried doing something similar to what you have done, in trying to
> make the compile fail for const objects, and it works, but I still don't
> understand why the compiler even looks at the ctor taking U& for r-values
> when const U& is there???

I hope that 14.7/8 will help to understand this delicate issue:
"If a function template or a member function template specialization is used
in a way that involves overload resolution, a declaration of the
specialization is *implicitly instantiated* (14.8.3)."

In case the instance is not well-formed then there are two possibilities:
1) Type deduction fail and the candidate will be eliminated. This is the
SFINAE case and it's deliberately limited as specified in 14.8.2/2
2) Type deduction will succeed and result with an ill-formed candidate that
will trigger compilation error.

SFINAE is limited since it's important not to hide too many substation
errors that might result with the elimination of the best candidate due to
programmer syntax error.
I actually abused this restriction by deliberately placing syntax error trap
for const auto_ptr arguments.
Notice that it is *not* relevant whether the ill-formed candidate is the
best viable one and in the auto_ptr in certainly isn't due to partial
ordering.
As you probably realized the auto_ptr simplified implementation is not
trivial to formally justify (yet trivial compared with the original one).

You might want to take a look at the following discussion:
http://tinyurl.com/yxki

> > Well, the smart creators of the current auto_ptr actually did
> > major step toward that goal probably without knowing it. Years later
Andrei
> > Alexandrescu discovered (also using wonderful tip by Dave Abrahams) the
> > potential of the auto_ptr technique and accomplished what you asked for
> > (perhaps with different goals):
> > http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/alexandr.htm
> >
>
> Thanks for the link ;-)
>
> I got to learn quite a lot from that!!! Yes, the technique for selecting
> temporaries is quite good, apart form the fact that non-const l-values are
> left in the dark.

I think that you meant for the const r-values which usually are strange
creatures.

Rani

Bronek Kozicki

unread,
Dec 12, 2003, 10:15:42 PM12/12/03
to
On Wed, 10 Dec 2003 18:22:50 +0000 (UTC), Bronek Kozicki wrote:
> On Mon, 1 Dec 2003 17:39:21 +0000 (UTC), "Rani Sharoni" wrote:
>> Again, Comments are more than welcome.
> There is another possible way of resolving undefined behaviour problem -
> store static information on dynamic type of pointed object, as done in
> shared_ptr [4]. I do not think that it's viable option in auto_ptr,

Apparently it is. Here is final version of proposed auto_ptr extension.
It has been tested under Comeau (online evaluation), MSVC71, GCC331
(MINGW). Proposed auto_ptr extension is reusing concept of deleter
class, which has been introduced in shared_ptr [1], however instead of
"operator()(X*)" it's calling static function "free (X*)". This is
consequence of small overhead - auto_ptr does not preserve state of
deleter object.

Two template classes del_functor and del_bind are provided to partly
overcome this limitation. Both will call "operator() const" of supplied
class.
* del_functor - will use static instance of functor object. This
instance may be created in advance by "init" static function.
* del_bind - will use existing instance of functor object. Instance is
passed by address; rules of non-template parameters apply, ie. functor
object must have external linkage
It's not possible to use local or temporary functor object as auto_ptr
deleter. Because all calls to functor object will share the same
instance of functor class, its operator() must have modifier "const".
Sample of how these limitations can be overcome is included - see
template class AdvF. Probably this all "functor" thing is bit too
complicated, but I added it to make auto_ptr partialy compatible with
deleters as defined in shared_ptr, without its overhead.

Proposed auto_ptr extension does not store internally copy of deleter
object passed to its constructor; it merely stores pointer to trampoline
function, which in turn calls static function of deleter class. Proposed
auto_ptr extension has very small overhead overexisting auto_ptr (only
pointer to trampoline function is stored, no heap allocation), as
discussed in my previous post in this thread.

I'm open for discussion


B.


[1] http://www.boost.org/libs/smart_ptr/sp_techniques.html


#include <cassert>
#include <cassert>
#include <cstdio>

// Following code is based on Rani Sharoni proposal
// to fix std::auto_ptr posted on comp.std.c++ on Dec 1st, 2003

//
// Proposed auto_ptr implementation begin
//

// Deleters are classes containing static function "free",
// which can be called in following form:
// Deleter::free(pointer);
// where type of "pointer" is static type of object
// contained in auto_ptr, as it was known at the point
// of auto_ptr construction from raw pointer.
//
// In example bellow:
// auto_ptr<Base> (new Derived);
// type of parameter passed to Deleter::free will be class being
// actual type of raw pointer, ie. "Derived".
//
// Deleter type is detected from second parameter of auto_ptr
// constructor. Simplest way is to pass it as temporary object,
// thus its constructor must not throw to prevent memory leaks.
// This object will not be preserved in auto_ptr object.
//
// The sole purspose of deleters is to delete object
// whose lifetime is managed by auto_ptr. It does not have to be
// template, however it must properly handle type of parameter.
//
// Object of auto_ptr class will store pointer to instantiation
// of its internal trampoline function, which will call deleter
// when object managed by auto_ptr has to be deleted. Copy
// construction and copy assignment of auto_ptr will preserve
// this pointer. This will ensure correct destruction of object
// owned by auto_ptr, even if it has been passed to auto_ptr
// whose type is its base class without virtual destructor.
//
// Basic deleters are included in this proposal. There are also simple
// adapters allowing limited use of functors

// Default deleter
struct del_pointer
{
template <typename X>
static void free(X* px)
{
delete px;
}
};

// Array deleter - optional part of proposal
struct del_array
{
template <typename X>
static void free(X* px)
{
delete[] px;
}
};

// Null deleter - optional part of proposal
struct del_null
{
template <typename X>
static void free(X* px)
{}
};

// del_functor - call const instance of functor object
template <typename F>
struct del_functor
{
static const F& init()
{
static const F f;
return f;
}

template <typename X>
static void free(X* px)
{
const F& f = init();
f(px);
}
};

// del_bind - preserve address of given functor object
template <typename F, const F* pf>
struct del_bind
{
template <typename X>
static void free(X* px)
{
pf->operator() (px);
}
};

template<class X>
class auto_ptr
{

private:
// Implementation detail : trampoline code to call deleter
template <typename W>
struct _trampoline_
{
static void _jump_(void * pv)


{
X* px = reinterpret_cast<X*> (pv);

W::free(px);
}
};

public:
typedef X element_type;

// 1. Default constructor. Using default deleter, example:
// auto_ptr<Class> (new Class);
explicit auto_ptr(X* p = 0) throw()
: ptr_(p),
del_(&_trampoline_<del_pointer>::_jump_)
{}

// 2. User defined deleter, static type of raw pointer is the same
// as type of auto_ptr template parameter. Example:
// auto_ptr<Class> (new Class, DeleterClass());
template <typename W>
explicit auto_ptr(X* p, const W& w) throw()
: ptr_(p),
del_(&_trampoline_<W>::_jump_)
{}

// 3. Static type of raw pointer is different than type of
// auto_ptr template parameter, default deleter. Example:
// auto_ptr<Base> (new Derived);


template <class V>
explicit auto_ptr(V* p = 0) throw()
: ptr_(p),

del_(&auto_ptr<V>::template _trampoline_<del_pointer>::_jump_)
{}

// 4. Static type of raw pointer is different than type of
// auto_ptr template parameter, custom deleter. Example:
// auto_ptr<Base> (new Derived, DeleterClass());
template <class V, typename W>
explicit auto_ptr(V* p, const W& w) throw()
: ptr_(p),
del_(&auto_ptr<V>::template _trampoline_<W>::_jump_)
{}

#ifndef _MSC_VER
auto_ptr(auto_ptr& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()),
del_(rhs.del_)
{}
#else
auto_ptr(auto_ptr const& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()),
del_(rhs.del_)
{}
#endif

template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()

: ptr_(const_cast<auto_ptr<U>&>(rhs).release()),
del_(rhs.del_)
{}

auto_ptr& operator=(auto_ptr& rhs) throw()
{
reset(rhs.release());
del_ = rhs.del_;
return *this;
}

template<class Y>


auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{

reset(rhs.release());
del_ = rhs.del_;
return *this;
}

~auto_ptr() throw()
{
reset();
}

// 20.4.5.2 members:
X& operator* () const throw()
{
assert(ptr_);
return *get();
}

X* operator->() const throw()
{
return &*(*this);
}

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

X* release() throw()
{
X* ptr = get();
ptr_ = 0;
return ptr;
}

void reset(X* p = 0) throw()
{
if (ptr_ != p)
{

assert(del_);
del_(get());
ptr_ = p;
}
}

private:
typedef void (* _free_t_) (void *);

X* ptr_;
_free_t_ del_; // pointer to deleter function

private:
template<class Y> friend class auto_ptr;

template<typename T> struct error_const_auto_ptr;

template<typename T> struct error_const_auto_ptr< auto_ptr<T> const >
{ typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };

template<class U>
auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
};

//
// Proposed auto_ptr implementation end
//

//
// Test cases
//

// Common base class B


struct B
{
B()
{
printf("B::B\t%p\n", this);
}
~B()
{
printf("B::~B\t%p\n\n", this);
}
};

// Class B has no virtual destructor, thus undefined behaviour
// will occur when object of class F or D is being deleted through
// pointer to B. On some compilers this undefined behaviour
// is to simply execute wrong destructor, which
// allows us to trace lifetime of our test objects.

struct D : B
{
D()
{
printf("D::D\t%p\n\n", this);
}
~D()
{
printf("D::~D\t%p\n", this);
}
};

// Class F has unusual deletion semantics
struct F : B
{
F()
{
printf("F::F\t%p\n\n", this);
}
void free() {delete this;}

private:
~F()
{
printf("F::~F\t%p\n", this);
}
};

// Custom deleter for class F
struct delete_f
{
static void free(F *p)
{
printf("f.free\t%p\n", p);
p->free();
}
};

// Template custom deleter
struct delete_tf
{
template <typename T>
static void free(T *p)
{
printf("tf.free\t%p\n", p);
p->free();
}
};

// Another template custom deleter
template <typename T>
struct delete_tc
{
static void free(T *p)
{
printf("tc.free\t%p\n", p);
p->free();
}
};

// Test source function


auto_ptr<D> source()
{
return auto_ptr<D>(new D);
}

// Test source function, safely returning pointer to static data
auto_ptr<D> source_static()
{
static D d;
return auto_ptr<D>(&d, del_null());
}

// Test sink function
void sink(auto_ptr<B>) {}

// functors used to test del_bind and del_functor
namespace {
struct Fctr
{
int i_;

Fctr(int i = 0) : i_(i) {}

template <typename X>
void operator() (X *px) const
{
printf("%i\n", i_);
delete px;
}

void operator() (F *pf) const
{
printf("%i(F)\n", i_);
pf->free();
}
};

Fctr fctr(10);
extern const Fctr fct2(15);
}

// more advanced functor class
template <typename X, int>
class AdvF
{
private:
mutable auto_ptr<X> fctor_;

public:
template <typename Y>
void operator() (Y* py) const
{
if (fctor_.get() != NULL)
fctor_->operator() (py);
}

void reset(auto_ptr<X> x) const
{
fctor_.reset(x.release());
}
};

int main()
{
{
// receive pointer to auto_ptr<D> from source
// and store it in auto_ptr<B>.


auto_ptr<B> b1 = source();
}

{
// create auto_ptr<D> and pass it to sink function
// taking auto_ptr<B>


auto_ptr<D> d1 = auto_ptr<D>(new D);
sink(d1);
}

{
// create pointer auto_ptr<F> to and
// store it in auto_ptr<B> . Class F has
// unusual deletion semantics

auto_ptr<B> b2 = auto_ptr<F>(new F, delete_f());
auto_ptr<B> b3 = auto_ptr<F>(0, delete_f());
auto_ptr<B> b4 = auto_ptr<B>(new F, delete_tf());
auto_ptr<B> b5 = auto_ptr<B>(new F, delete_tc<F>());
// error: 'free' is not a member of 'B'
// auto_ptr<B> b6 = auto_ptr<B>(new F, delete_tc<B>());
// error: cannot access private member declared in class 'F'
// auto_ptr<B> b7 = auto_ptr<F>(0);
// error: cannot access private member declared in class 'F'
// auto_ptr<F> b8 = auto_ptr<F>(new F);
}

{
// instantiate deleter with static type of raw pointer
// ie. "D" (constructor 3.)
auto_ptr<B> b9 = auto_ptr<B>(new D);
}

{
// pathological case: auto_ptr does not know dynamic type of
// stored pointer, thus will not be able to delete it safely
// B* b10raw = new D;
// auto_ptr<B> b10 = auto_ptr<B>(b10raw);
}

{
// example use of default array deleter.
auto_ptr<B> b11 = auto_ptr<B>(new D[3], del_array());
auto_ptr<B> b12 = auto_ptr<B>(new B[2], del_array());
// error: cannot access private member declared in class 'F'
// class destructor must be accessible in C-style array
// auto_ptr<B> b13 = auto_ptr<B>(new F[2], del_array);
}

{
// example use of null deleter
D d2auto;
auto_ptr<B> b14 = auto_ptr<D>(&d2auto, del_null());
sink(b14);

auto_ptr<B> b15 = source_static();
}

{
// example use of functor binders
auto_ptr<B> b1 = auto_ptr<D>(new D, del_functor<Fctr>());
// del_bind and del_bind_const will preserve address of functor
auto_ptr<B> b5 = auto_ptr<D>(new D, del_bind<Fctr, &fctr>());
auto_ptr<B> b6 = auto_ptr<D>(new D, del_bind<Fctr, &fctr>());
auto_ptr<B> b7 = auto_ptr<D>(new D, del_bind<Fctr, &fct2>());
// use of overloaded operator() defined in functor object
auto_ptr<B> b9 = auto_ptr<B>(new F, del_functor<Fctr>());
auto_ptr<B> b10 = auto_ptr<B>(new F, del_bind<Fctr, &fct2>());
auto_ptr<B> b11 = auto_ptr<F>(new F, del_bind<Fctr, &fctr>());
// explicitly init functor object Fctr before its usedby deleter;
// recommended, if constructor of functor object can throw.
del_functor<Fctr>::init();
// overcome some deleter limitations
auto_ptr<B> b12 = auto_ptr<B>(new F, del_functor<AdvF<Fctr, 0> >());
auto_ptr<B> b13 = auto_ptr<B>(new D, del_functor<AdvF<Fctr, 0> >());
auto_ptr<B> b14 = auto_ptr<B>(new D, del_functor<AdvF<Fctr, 1> >());
del_functor<AdvF<Fctr, 0> >::init().reset(auto_ptr<Fctr> (new
Fctr(30)));
del_functor<AdvF<Fctr, 1> >::init().reset(auto_ptr<Fctr> (new
Fctr(25)));

David Abrahams

unread,
Dec 12, 2003, 10:17:11 PM12/12/03
to
dhru...@gmx.net ("Dhruv") writes:

> On Fri, 05 Dec 2003 21:55:25 +0000, David Abrahams wrote:
>
>> rani_s...@hotmail.com ("Rani Sharoni") writes:
>>
>>> Another issue that was probably overlooked is auto_ptr_ref exception safety
>>> issue:
>>>
>>> int f(auto_ptr<B>, std::string);
>>> auto_ptr<B> source();
>>>
>>> int x = f(source(), std::string("xyz"));
>>>
>>> I'm sure that you, my exception safety mentor, will immediately understand
>>> the problem and not be surprise to hear that it actually exists in all the
>>> STL implementations (although compilers probably generates safe code).
>>
>> Whoops. Yep you're right. This one is nasty. The compiler can
>> reorganize things so that the string is constructed while only the
>> auto_ptr_ref has a hold on the pointer. DR, DR, DR :(.
>>
>
> I'm no expert, but just as a thought, can we for the time being, remedy
> the situation by delaying the transfer of ownership by not constructing
> auto_ptr_ref with a pointer, but instead by a const reference to auto_ptr,
> and store that in the data section of auto_ptr_ref as a reference, then we
> can delay the transfer of ownership upto the time when it's time for the
> destination to get the ownership.

Yes of course. You can even make work it work the way we want
arguably within the bounds of the current standard language by
"holding" the pointer by non-const reference in auto_ptr_ref, or by
giving auto_ptr_ref auto_ptr-like transfer of ownership semantics.

The problem is that the current standard language makes it legal for
implementations to do something naive that leaks in the case above.

I'm not convinced it makes sense to fiddle around with auto_ptr_ref
any more. It doesn't do what we intended; if there's an
implementation of auto_ptr which does, we should just replace it.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---

Dhruv

unread,
Dec 13, 2003, 1:19:59 AM12/13/03
to
On Fri, 12 Dec 2003 19:51:21 +0000, Rani Sharoni wrote:

> ""Dhruv"" <dhru...@gmx.net> wrote in message
> news:pan.2003.12.11....@gmx.net...
>> Then, I tried doing something similar to what you have done, in trying to
>> make the compile fail for const objects, and it works, but I still don't
>> understand why the compiler even looks at the ctor taking U& for r-values
>> when const U& is there???
>
> I hope that 14.7/8 will help to understand this delicate issue:
> "If a function template or a member function template specialization is used
> in a way that involves overload resolution, a declaration of the
> specialization is *implicitly instantiated* (14.8.3)."
>

Ok.

> In case the instance is not well-formed then there are two possibilities:
> 1) Type deduction fail and the candidate will be eliminated. This is the
> SFINAE case and it's deliberately limited as specified in 14.8.2/2
> 2) Type deduction will succeed and result with an ill-formed candidate that
> will trigger compilation error.
>
> SFINAE is limited since it's important not to hide too many substation
> errors that might result with the elimination of the best candidate due to
> programmer syntax error.
> I actually abused this restriction by deliberately placing syntax error trap
> for const auto_ptr arguments.
> Notice that it is *not* relevant whether the ill-formed candidate is the
> best viable one and in the auto_ptr in certainly isn't due to partial
> ordering.

Ok, this is important... I did no know that such a thing existed.

> As you probably realized the auto_ptr simplified implementation is not
> trivial to formally justify (yet trivial compared with the original one).
>
> You might want to take a look at the following discussion:
> http://tinyurl.com/yxki
>

Ok, that explains that... BTW, how do you get that tinyurl.com???

>> I got to learn quite a lot from that!!! Yes, the technique for selecting
>> temporaries is quite good, apart form the fact that non-const l-values are
>> left in the dark.
>
> I think that you meant for the const r-values which usually are strange
> creatures.

I meant for the mojo_ptr, the assignable part is not there, since the
reference constructor is not there, so copying from l-values is not there.
You are obviously talking about the other issue.

Regards,
-Dhruv.

Dhruv

unread,
Dec 13, 2003, 4:37:56 PM12/13/03
to
On Fri, 12 Dec 2003 19:51:21 +0000, Rani Sharoni wrote:

> ""Dhruv"" <dhru...@gmx.net> wrote in message
> news:pan.2003.12.11....@gmx.net...
>> Then, I tried doing something similar to what you have done, in trying to
>> make the compile fail for const objects, and it works, but I still don't
>> understand why the compiler even looks at the ctor taking U& for r-values
>> when const U& is there???
>
> I hope that 14.7/8 will help to understand this delicate issue:
> "If a function template or a member function template specialization is used
> in a way that involves overload resolution, a declaration of the
> specialization is *implicitly instantiated* (14.8.3)."
>

Ok.

> In case the instance is not well-formed then there are two possibilities:
> 1) Type deduction fail and the candidate will be eliminated. This is the
> SFINAE case and it's deliberately limited as specified in 14.8.2/2
> 2) Type deduction will succeed and result with an ill-formed candidate that
> will trigger compilation error.
>
> SFINAE is limited since it's important not to hide too many substation
> errors that might result with the elimination of the best candidate due to
> programmer syntax error.
> I actually abused this restriction by deliberately placing syntax error trap
> for const auto_ptr arguments.
> Notice that it is *not* relevant whether the ill-formed candidate is the
> best viable one and in the auto_ptr in certainly isn't due to partial
> ordering.

Ok, this is important... I did no know that such a thing existed.

> As you probably realized the auto_ptr simplified implementation is not


> trivial to formally justify (yet trivial compared with the original one).
>
> You might want to take a look at the following discussion:
> http://tinyurl.com/yxki
>

Ok, that explains that... BTW, how do you get that tinyurl.com???

>> I got to learn quite a lot from that!!! Yes, the technique for selecting


>> temporaries is quite good, apart form the fact that non-const l-values are
>> left in the dark.
>
> I think that you meant for the const r-values which usually are strange
> creatures.

I meant for the mojo_ptr, the assignable part is not there, since the


reference constructor is not there, so copying from l-values is not there.
You are obviously talking about the other issue.

Regards,
-Dhruv.

---

Rani Sharoni

unread,
Dec 14, 2003, 3:36:02 PM12/14/03
to
"Dhruv" wrote:
> On Fri, 05 Dec 2003 21:55:25 +0000, David Abrahams wrote:
>
>> rani_s...@hotmail.com ("Rani Sharoni") writes:
>>
>>> Another issue that was probably overlooked is auto_ptr_ref
>>> exception safety issue:
>>>
>>> int f(auto_ptr<B>, std::string);
>>> auto_ptr<B> source();
>>>
>>> int x = f(source(), std::string("xyz"));
>>>
>>> I'm sure that you, my exception safety mentor, will immediately
>>> understand the problem and not be surprise to hear that it actually
>>> exists in all the STL implementations (although compilers probably
>>> generates safe code).
>>
>> Whoops. Yep you're right. This one is nasty. The compiler can
>> reorganize things so that the string is constructed while only the
>> auto_ptr_ref has a hold on the pointer. DR, DR, DR :(.
>>
> [...]

> //Auto_Ptr_Ref
> template<typename _Tp1>
> struct auto_ptr_ref
> {
> auto_ptr<_Tp1>& _M_ptr;
>
> explicit
> auto_ptr_ref(auto_ptr<_Tp1> const& __p)
> : _M_ptr(const_cast<auto_ptr<_Tp1>& >(__p)) {}
> };

>
> template<typename _Tp1>
> operator auto_ptr_ref<_Tp1>() throw()
> {
> // return auto_ptr_ref<_Tp1>(this->release()); }
> return auto_ptr_ref<_Tp1>(*this); }
>
> auto_ptr(auto_ptr_ref<element_type> __ref) throw()
> : _M_ptr(__ref._M_ptr) {
> //Added the line below.
> __ref._M_ptr.release ();
> }
>
> //And for the assignment operator.
>
> I cannot see any obvious defects in the above, but I could always have
> overlooked something. Please let me know if there is anything
> seriously wrong, because I intend using this...

I think that you gained enough knowledge to *carefully* analyze the
following case:
auto_ptr<B> bp(auto_ptr<D>(0));

The current standard actually requires something similar to your suggestion
and as I specified in the DR this requirement contains major flow after TC
DR #127 that kicked auto_ptr_ref to the other scope without revising
20.4.5/2.

Hint - The above contains binding reference to temporary which eventually
ends up with returning reference to local variable. Oops.
In case that you are not using the new proposal, which eliminates the need
of auto_ptr_ref, then the code will not compile.

Removing the const from auto_ptr_ref(auto_ptr<_Tp1> const& __p) which is the
obvious way to comply to 20.4.5/2 results with another form of catastrophe
which is infinite recursion and is even more "fun" to analyzing. Hint -
auto_ptr_ref *copy* constructor play important role in this case.

The original auto_ptr_ref was private member of auto_ptr and the intention
beind 20.4.5/2 was that the implementation will be:

template<class X>
class auto_ptr
{

template <class Y>
struct auto_ptr_ref
{
auto_ptr_ref(auto_ptr<X>& p)
: p_(p)
{}

auto_ptr<X>& p_;
};

public:
// [...]

template<typename Y>
operator auto_ptr_ref<Y>() throw()
{
return auto_ptr_ref<Y>(*this);
}
};

Which "works" tremendously without testing. See DR #127.

auto_ptr is probably good lesson for up and coming C++ gurus and also
remarkable demonstrations that even the most "obvious" working facility must
be tested.

The approach of the new implementation technique is different especially
since, unlike the original implementation, it basically allows everything in
the most obvious way and then forbids the unwanted cases using
specialization.

Rani

Ben Hutchings

unread,
Dec 15, 2003, 3:25:31 PM12/15/03
to
Bronek Kozicki wrote:
> On Wed, 10 Dec 2003 18:22:50 +0000 (UTC), Bronek Kozicki wrote:
>> On Mon, 1 Dec 2003 17:39:21 +0000 (UTC), "Rani Sharoni" wrote:
>>> Again, Comments are more than welcome.
>> There is another possible way of resolving undefined behaviour problem -
>> store static information on dynamic type of pointed object, as done in
>> shared_ptr [4]. I do not think that it's viable option in auto_ptr,
>
> Apparently it is. Here is final version of proposed auto_ptr extension.
> It has been tested under Comeau (online evaluation), MSVC71, GCC331
> (MINGW). Proposed auto_ptr extension is reusing concept of deleter
> class, which has been introduced in shared_ptr [1], however instead of
> "operator()(X*)" it's calling static function "free (X*)".

Unfortunately you have not used it correctly.

<snip>

> struct _trampoline_
> {
> static void _jump_(void * pv)
> {
> X* px = reinterpret_cast<X*> (pv);
> W::free(px);
> }
> };

<snip>


> // 1. Default constructor. Using default deleter, example:
> // auto_ptr<Class> (new Class);
> explicit auto_ptr(X* p = 0) throw()
> : ptr_(p),
> del_(&_trampoline_<del_pointer>::_jump_)
> {}

This initialises del_ with a pointer to a _jump_ function that
takes a pointer to type X.

<snip>


> template<class U>
> auto_ptr(auto_ptr<U> const& rhs) throw()
> : ptr_(const_cast<auto_ptr<U>&>(rhs).release()),
> del_(rhs.del_)
> {}

<snip>

This conversion constructor template can then pass the same
_jump_ function into an auto_ptr to another type, while
converting ptr_.

So when it comes to deletion, a pointer of type Y * is
implicitly converted to void * and then cast to type X *.
This is *not* equivalent to a direct conversion from Y * to
X * which is what should be done.

Your test cases happen to work by chance, but this test will
produce a satisfying explosion when compiled with VC++ 7.1
and run:

struct B1
{
B1()
{
printf("B1::B1\t%p\n", (void *)this);
}
virtual ~B1()
{
printf("B1::~B1\t%p\n\n", (void *)this);
}
};

struct B2
{
B2()
{
printf("B2::B2\t%p\n", (void *)this);
}
virtual dummy()
{
printf("B2::dummy\t%p\n", (void *)this);
}
virtual ~B2()
{
printf("B2::~B2\t%p\n\n", (void *)this);
}
};

struct D : B1, B2
{
};

int main()
{
auto_ptr<D> pd(new D);
auto_ptr<B2> pb2(pd);
pb2.reset();

Bronek Kozicki

unread,
Dec 16, 2003, 7:41:29 PM12/16/03
to
On Mon, 15 Dec 2003 20:25:31 +0000 (UTC), Ben Hutchings wrote:
> So when it comes to deletion, a pointer of type Y * is
> implicitly converted to void * and then cast to type X *.
> This is *not* equivalent to a direct conversion from Y * to
> X * which is what should be done.
>
> Your test cases happen to work by chance, but this test will
> produce a satisfying explosion when compiled with VC++ 7.1
> and run:

Thank you for pointing this out, I can see the problem. This can be
fixed by adding another member data, ie. original pointer as it was
passed to constructor from raw pointer, cast to void*. I've added "void*
ptv_" member, its handling in constructors, reset and operator=. There
were some more problems with reset, which I have fixed and added
template version. I also slightly modified del_functor and del_bind
template classes (deleter adapters).

Of course additional data member adds even more overhead (3 pointers
instead of 1), and I'm not sure if such "bulky" auto_ptr can still be
accepted. Nevertheless, I'd like to kindly ask readers to examine
following auto_ptr proposal. Maybe it makes more sense to rename it and
pass to boost ?

Regards


B.

// Following code is based on Rani Sharoni proposal
// to fix std::auto_ptr posted on comp.std.c++ on Dec 1st, 2003

//
// Proposed auto_ptr implementation begin
//

// Default deleter


struct del_pointer
{
template <typename X>
static void free(X* px)
{
delete px;
}
};

// Array deleter - optional part of proposal
struct del_array
{
template <typename X>
static void free(X* px)
{
delete[] px;
}
};

// Null deleter - optional part of proposal
struct del_null
{
template <typename X>
static void free(X* px)
{}
};

// del_functor - call const instance of functor object
template <typename F>
struct del_functor
{

static F& instance()
{
static F f;
return f;
}

template <typename X>
static void free(X* px)
{

F& f = instance();
f(px);
}
};

// del_bind - preserve address of given functor object

template <typename F, F* pf>


struct del_bind
{
template <typename X>
static void free(X* px)
{
pf->operator() (px);
}
};

template<class X>
class auto_ptr
{
private:
// Implementation detail : trampoline code to call deleter
template <typename W>

struct _trampoline_
{
static void _jump_(void * pv)
{
X* px = reinterpret_cast<X*> (pv);
W::free(px);
}
};

public:
typedef X element_type;

explicit auto_ptr(X* p = 0) throw()
: ptr_(p) ,

ptv_(p),
del_(&_trampoline_<del_pointer>::_jump_)
{}

template <typename W>
explicit auto_ptr(X* p, const W& w) throw()
: ptr_(p),

ptv_(p),
del_(&_trampoline_<W>::_jump_)
{}

template <class V>
explicit auto_ptr(V* p = 0) throw()
: ptr_(p),
ptv_(p),


del_(&auto_ptr<V>::template _trampoline_<del_pointer>::_jump_)
{}

template <class V, typename W>


explicit auto_ptr(V* p, const W& w) throw()
: ptr_(p),

ptv_(p),


del_(&auto_ptr<V>::template _trampoline_<W>::_jump_)
{}

#ifndef _MSC_VER
auto_ptr(auto_ptr& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()),

ptv_(rhs.ptv_),


del_(rhs.del_)
{}
#else
auto_ptr(auto_ptr const& rhs) throw()
: ptr_(const_cast<auto_ptr&>(rhs).release()),

ptv_(rhs.ptv_),
del_(rhs.del_)
{}
#endif


template<class U>
auto_ptr(auto_ptr<U> const& rhs) throw()
: ptr_(const_cast<auto_ptr<U>&>(rhs).release()),

ptv_(rhs.ptv_),
del_(rhs.del_)
{}

auto_ptr& operator=(auto_ptr& rhs) throw()
{
reset(rhs.release());

ptv_ = rhs.ptv_;


del_ = rhs.del_;
return *this;
}

template<class Y>
auto_ptr& operator=(auto_ptr<Y> rhs) throw()
{
reset(rhs.release());

ptv_ = rhs.ptv_;


del_ = rhs.del_;
return *this;
}

~auto_ptr() throw()
{
reset();
}

// 20.4.5.2 members:
X& operator* () const throw()
{
assert(ptr_);
return *get();
}

X* operator->() const throw()
{
return &*(*this);
}

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

X* release() throw()
{
X* ptr = get();
ptr_ = 0;
return ptr;
}

void reset() throw()
{
if (ptr_)
{
assert(del_);
del_(ptv_);
ptr_ = 0;
}
}

void reset(X* p) throw()
{
if (ptr_ != p)
{
reset();
ptr_ = p;
ptv_ = p;
del_ = &_trampoline_<del_pointer>::_jump_;
}
}

template<class Y>
void reset(Y* p) throw()
{
if (ptr_ != p)
{
reset();
ptr_ = p;
ptv_ = p;
del_ = &auto_ptr<Y>::template _trampoline_<del_pointer>::_jump_;
}
}

template <typename W>
void reset(X* p, const W& w) throw()
{
if (ptr_ != p)
{
reset();
ptr_ = p;
ptv_ = p;
del_ = &_trampoline_<W>::_jump_;
}
}

template<class Y, typename W>
void reset(Y* p, const W& w) throw()
{
if (ptr_ != p)
{
reset();
ptr_ = p;
ptv_ = p;
del_ = &auto_ptr<Y>::template _trampoline_<W>::_jump_;
}
}

private:
typedef void (* _free_t_) (void *);

X* ptr_;
void* ptv_;


_free_t_ del_; // pointer to deleter function

private:
template<class Y> friend class auto_ptr;

template<typename T> struct error_const_auto_ptr;

template<typename T> struct error_const_auto_ptr< auto_ptr<T> const >
{ typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };

template<class U>
auto_ptr(U& rhs, typename error_const_auto_ptr<U>::type = 0);
};

//
// Proposed auto_ptr implementation end
//

---

Dhruv

unread,
Dec 21, 2003, 11:56:22 PM12/21/03
to
On Sun, 14 Dec 2003 20:36:02 +0000, Rani Sharoni wrote:

[snippety snippety snip]

> I think that you gained enough knowledge to *carefully* analyze the
> following case:
> auto_ptr<B> bp(auto_ptr<D>(0));
>
> The current standard actually requires something similar to your suggestion
> and as I specified in the DR this requirement contains major flow after TC
> DR #127 that kicked auto_ptr_ref to the other scope without revising
> 20.4.5/2.

I can't see anything wrong with that? Anything I missed out.

> Hint - The above contains binding reference to temporary which eventually
> ends up with returning reference to local variable. Oops.
> In case that you are not using the new proposal, which eliminates the need
> of auto_ptr_ref, then the code will not compile.

Are you talking about your proposal? Well, yes I an familiar with it,
but I do not know how fast it will be standardized, but since it looks
pretty fine (well, I'm no one to judge it!), I guess it should find it's
way into the holy standard.


> Removing the const from auto_ptr_ref(auto_ptr<_Tp1> const& __p) which is the
> obvious way to comply to 20.4.5/2 results with another form of
> catastrophe

To comply with 20.4.5/2, I do not see any requirement that the ctor
should take the parameter by reference, not const reference. Am I
missing something. It only says that it should store a reference to
auto_ptr.

> which is infinite recursion and is even more "fun" to analyzing. Hint -
> auto_ptr_ref *copy* constructor play important role in this case.

I have understood this way. Please correct me if I'm wrong:
Conversion function to auto_ptr_ref<Other_Type> is called from
auto_ptr<This_Type>. Then, when *this is passed to the ctor of
auto_ptr_ref, which takes a reference (non-const) to
auto_ptr<Other_Type>, again the conversion function is called, and the
cycle repeats itself unto the death of the run-time stack.


> The original auto_ptr_ref was private member of auto_ptr and the intention
> beind 20.4.5/2 was that the implementation will be:
>
> template<class X>
> class auto_ptr
> {
> template <class Y>
> struct auto_ptr_ref
> {
> auto_ptr_ref(auto_ptr<X>& p)
> : p_(p)
> {}
>
> auto_ptr<X>& p_;
> };
>
> public:
> // [...]
>
> template<typename Y>
> operator auto_ptr_ref<Y>() throw()
> {
> return auto_ptr_ref<Y>(*this);
> }
> };
>
> Which "works" tremendously without testing. See DR #127.

Yes, I saw it, and yes it does suffer from quite a few defects.


> auto_ptr is probably good lesson for up and coming C++ gurus and also
> remarkable demonstrations that even the most "obvious" working facility must
> be tested.

I can see that ;-)

> The approach of the new implementation technique is different especially
> since, unlike the original implementation, it basically allows everything in
> the most obvious way and then forbids the unwanted cases using
> specialization.

Regards,
-Dhruv.

Rani Sharoni

unread,
Dec 22, 2003, 9:07:21 PM12/22/03
to
dhru...@gmx.net ("Dhruv") wrote in message news:<pan.2003.12.21....@gmx.net>...

> On Sun, 14 Dec 2003 20:36:02 +0000, Rani Sharoni wrote:
>
> [snippety snippety snip]
>
> > I think that you gained enough knowledge to *carefully* analyze the
> > following case:
> > auto_ptr<B> bp(auto_ptr<D>(0));
> >
> > The current standard actually requires something similar to your suggestion
> > and as I specified in the DR this requirement contains major flow after TC
> > DR #127 that kicked auto_ptr_ref to the other scope without revising
> > 20.4.5/2.
>
> I can't see anything wrong with that? Anything I missed out.

Let's theoretically assume that it compiles. In this case the
constructor of auto_ptr_ref that you have suggested holds *local
temporary* and since you returned it as an argument you actually
returned reference to local variable. Yet another auto_ptr victim.

This case is more amusing then what I thought.
Consider the following code:
#include <memory>

struct B { virtual ~B(); };
struct D : B {};

int f(std::auto_ptr<B> const&);

int x1 = f(std::auto_ptr<B>()); // #1
int x2 = f(std::auto_ptr<D>()); // #2

I thought that it's not clear whether #1 is legal (IMO it's legal) but
I was certain that #2, your case, is illegal.

GCC accepted both cases
VC rejected both cases
EDG (Comeau online) had interesting results. Rejected #1 and
*accepted* #2.
I run "private" investigation (i.e. making methods private) and found
out that EDG and GCC applied the following conversion sequence:
auto_ptr<D> -> auto_ptr<B> (via auto_ptr<D>::operator auto_ptr<B>())
-> auto_ptr_ref<B> -> auto_ptr<B> (via auto_ptr<B>(auto_ptr_ref<B>))
-> bounded to auto_ptr<B>&

This obviously violates DR #84 and reveals that even when you write a
DR (and the standard) it's not trivial to enforce it ;-)
This evil facility doesn't take hostages.

What's bothers me is that EDG rejected #1 which is actually allowed by
8.5.3/3 and this has significant effect on Andrei's mojo usability.
See DR #291 for more details.

When I said that your code should not compile I had DR #84 in mind.



>> Removing the const from auto_ptr_ref(auto_ptr<_Tp1> const& __p)
which is the
> > obvious way to comply to 20.4.5/2 results with another form of
> > catastrophe
>
> To comply with 20.4.5/2, I do not see any requirement that the ctor
> should take the parameter by reference, not const reference. Am I
> missing something. It only says that it should store a reference to
> auto_ptr.

20.4.5.3/3 is consistent with auto_ptr standard over-specifications:
template<class Y> operator auto_ptr_ref<Y>() throw();
3 Returns: An auto_ptr_ref<Y> that holds *this.



> > which is infinite recursion and is even more "fun" to analyzing. Hint -
> > auto_ptr_ref *copy* constructor play important role in this case.
>
> I have understood this way. Please correct me if I'm wrong:
> Conversion function to auto_ptr_ref<Other_Type> is called from
> auto_ptr<This_Type>. Then, when *this is passed to the ctor of
> auto_ptr_ref, which takes a reference (non-const) to
> auto_ptr<Other_Type>, again the conversion function is called, and the
> cycle repeats itself unto the death of the run-time stack.

Exactly.

Rani

Rani Sharoni

unread,
Dec 26, 2003, 2:03:10 PM12/26/03
to
rani_s...@hotmail.com (Rani Sharoni) wrote in message
> Consider the following code:
> #include <memory>
>
> struct B { virtual ~B(); };
> struct D : B {};
>
> int f(std::auto_ptr<B> const&);
>
> int x1 = f(std::auto_ptr<B>()); // #1
> int x2 = f(std::auto_ptr<D>()); // #2
>
> I thought that it's not clear whether #1 is legal (IMO it's legal) but
> I was certain that #2, your case, is illegal.
>
> GCC accepted both cases
> VC rejected both cases
> EDG (Comeau online) had interesting results. Rejected #1 and
> *accepted* #2.
> I run "private" investigation (i.e. making methods private) and found
> out that EDG and GCC applied the following conversion sequence:
> auto_ptr<D> -> auto_ptr<B> (via auto_ptr<D>::operator auto_ptr<B>())
> -> auto_ptr_ref<B> -> auto_ptr<B> (via auto_ptr<B>(auto_ptr_ref<B>))
> -> bounded to auto_ptr<B>&
>
> This obviously violates DR #84 and reveals that even when you write a
> DR (and the standard) it's not trivial to enforce it ;-)
> This evil facility doesn't take hostages.

"The core language auto_ptr problem"
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf

Enjoy,

Dhruv

unread,
Dec 26, 2003, 2:03:23 PM12/26/03
to
On Tue, 23 Dec 2003 02:07:21 +0000, Rani Sharoni wrote:

> dhru...@gmx.net ("Dhruv") wrote in message news:<pan.2003.12.21....@gmx.net>...
>> On Sun, 14 Dec 2003 20:36:02 +0000, Rani Sharoni wrote:
>>
>> [snippety snippety snip]
>>
>> > I think that you gained enough knowledge to *carefully* analyze the
>> > following case:
>> > auto_ptr<B> bp(auto_ptr<D>(0));
>> >
>> > The current standard actually requires something similar to your suggestion
>> > and as I specified in the DR this requirement contains major flow after TC
>> > DR #127 that kicked auto_ptr_ref to the other scope without revising
>> > 20.4.5/2.
>>
>> I can't see anything wrong with that? Anything I missed out.
>
> Let's theoretically assume that it compiles. In this case the
> constructor of auto_ptr_ref that you have suggested holds *local
> temporary* and since you returned it as an argument you actually
> returned reference to local variable. Yet another auto_ptr victim.

No, unless I'm not mistaken. I guess this is how it works:

auto_ptr_ref gets initialized by reference to temporary. Then, auto_ptr
(another temporary) gets constructed from auto_ptr_ref that holds the
reference to temporary that is still undestructed. Then the ctor for the
temprary that the compiler invented for return calls release and makes the
original local variable relinquish ownership, and itself becomes the
owner. Then finally, the local variable gets destroyed. That seem fine to
me. Unless of course, I have overlooked some important mechanism.

Just to ensure that we are both on the same wavelength, let me post some
code. I'm talking about code like this. I hope that's what you're talking
about:

auto_ptr<int> get_ptr (int value)
{
auto_ptr<int> temp (new int(value));
return temp;
}


> This case is more amusing then what I thought.
> Consider the following code:
> #include <memory>
>
> struct B { virtual ~B(); };
> struct D : B {};
>
> int f(std::auto_ptr<B> const&);
>
> int x1 = f(std::auto_ptr<B>()); // #1
> int x2 = f(std::auto_ptr<D>()); // #2
>
> I thought that it's not clear whether #1 is legal (IMO it's legal) but
> I was certain that #2, your case, is illegal.

Even I guess it should work.

GCC accepted both cases <--- That's all I can test on.

> VC rejected both cases
> EDG (Comeau online) had interesting results. Rejected #1 and
> *accepted* #2.
> I run "private" investigation (i.e. making methods private) and found
> out that EDG and GCC applied the following conversion sequence:
> auto_ptr<D> -> auto_ptr<B> (via auto_ptr<D>::operator auto_ptr<B>())
> -> auto_ptr_ref<B> -> auto_ptr<B> (via auto_ptr<B>(auto_ptr_ref<B>))
> -> bounded to auto_ptr<B>&
>
> This obviously violates DR #84 and reveals that even when you write a
> DR (and the standard) it's not trivial to enforce it ;-)
> This evil facility doesn't take hostages.

Hmmm. this is bad.

> What's bothers me is that EDG rejected #1 which is actually allowed by
> 8.5.3/3 and this has significant effect on Andrei's mojo usability.
> See DR #291 for more details.

If what you said earlier is true, then I think that mojo should not hold,
because it works on more or less the same principle, but as I have
explained, it may not be correct.

> When I said that your code should not compile I had DR #84 in mind.
>
>>> Removing the const from auto_ptr_ref(auto_ptr<_Tp1> const& __p)
> which is the
>> > obvious way to comply to 20.4.5/2 results with another form of
>> > catastrophe
>>
>> To comply with 20.4.5/2, I do not see any requirement that the ctor
>> should take the parameter by reference, not const reference. Am I
>> missing something. It only says that it should store a reference to
>> auto_ptr.
>
> 20.4.5.3/3 is consistent with auto_ptr standard over-specifications:
> template<class Y> operator auto_ptr_ref<Y>() throw();
> 3 Returns: An auto_ptr_ref<Y> that holds *this.

You've got to accept it, that I'm thick in the head at times. I still
don't get the relation!!! It's Christmas, and still the bellas ain't
ringing!!!

>> > which is infinite recursion and is even more "fun" to analyzing. Hint -
>> > auto_ptr_ref *copy* constructor play important role in this case.
>>
>> I have understood this way. Please correct me if I'm wrong:
>> Conversion function to auto_ptr_ref<Other_Type> is called from
>> auto_ptr<This_Type>. Then, when *this is passed to the ctor of
>> auto_ptr_ref, which takes a reference (non-const) to
>> auto_ptr<Other_Type>, again the conversion function is called, and the
>> cycle repeats itself unto the death of the run-time stack.
>
> Exactly.


Merry Christmas,
-Dhruv.

0 new messages