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