Topic: auto_ptr<T> as function return type


Author: jason@cygnus.com (Jason Merrill)
Date: 1995/11/15
Raw View
Distribution:
>>>>> Sean A Corfield <sean@corf.demon.co.uk> writes:

> Similar code changes could be done for operator=. The committee have not
> decided exactly what to do about fixing auto_ptr yet but they intend to do
> _something_ in Scotts Valley...probably just make the parameter of the copy
> constructor const and force the implementation to work around it with a
> cast (ugh!).

Or using mutable...

Jason


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: jodle@bix.com (jodle)
Date: 1995/11/15
Raw View
Ralph Shnelvar (ralphs@tesser.com) wrote:

: Let there be a #pragma or -something- that turns off this awful
: creation of temporaries.

I think it would be a bad thing for the standard to prescribe what pragmas
a compiler will support.  It's sufficient that it doesn't preclude them.
Furthermore, privately declared copy constructors work wonders in this
regard.  I've found that where programmers say they don't want temporaries
generated, what they really don't want is their copy implementations
stressed.


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: Sean A Corfield <sean@corf.demon.co.uk>
Date: 1995/11/15
Raw View
In article <u9ivkmiw9y.fsf@yorick.cygnus.com>,
jason@cygnus.com (Jason Merrill) wrote:

|> Or using mutable...

Jerry Schwarz (who proposed mutable) has said several times that he doesn't
think mutable should be used simply to avoid a const_cast<> and I agree
with him. mutable was intended to allow concrete and abstract 'const'
semantics to be implemented directly in the language. Making auto_ptr<>'s
internal pointer mutable would be a lie by that logic: the auto_ptr<> is
_not_ retaining abstract const-ness -- it really does change the external
semantics when you copy one.




---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: phalpern@truffle.ultranet.com (Pablo Halpern)
Date: 1995/11/18
Raw View
Sean A Corfield <sean@corf.demon.co.uk> wrote:

>In Tokyo last week, Bill Gibbons suggested using a proxy nested class to
>alleviate this problem. It works by having auto_ptr constructable from the
>proxy object and assignable from the proxy object then having auto_ptr
>convert to the proxy. Something like this:
>
>  template<typename T> class auto_ptr {
>    struct proxy {
>      auto_ptr* p;
>      proxy(auto_ptr* ap) : p(ap) { }
>    };
>  public:
>    auto_ptr(const proxy& prox) : p(prox.p->p) { prox.p->p = 0; }
>    // etc
>    operator proxy() { return proxy(this); }
>    // etc
>  private:
>    T* p;
>  };
>
>
>  auto_ptr<X> f();
>  auto_ptr<X> p(f());
>  // equivalent to:
>  auto_ptr<X> p(f().operator proxy());
>
>Similar code changes could be done for operator=.

Wouldn't it be more efficient to code the so-called "copy constructor"
using pass by value to avoid the extra unnamed temporary:

  auto_ptr(proxy prox) : p(prox.p->p) { prox.p->p = 0; }

This way, the generated code should be exactly as efficient as passing a
non-const auto_ptr by reference. My concern with both approaches is that
the WP does not actually consider the above to be true copy constructors
(although the ARM did). Thus, the compiler may generate its own copy
constructor for auto_ptr<T>, thus defeating the whole thing.

Just for completeness, I'll mention another solution, ugly as it is:

  template<typename T> class auto_ptr {
  public:
    auto_ptr() : pp(new T*(0)) { }  // pp = Pointer to null pointer
    auto_ptr(const auto_ptr<T>& rhs) : pp(new T*(*rhs.pp))
      { *rhs.pp = 0; }
    ~auto_ptr() { delete *pp; delete pp; } // double delete
    T* release() { T* p = *pp; *pp = 0; return p; }

    auto_ptr<T>& operator=(const auto_ptr<T>& rhs)
    {
      if (this == &rhs) return *this;
      delete *pp;
      *pp = *rhs.pp;
      *rhs.pp = 0;  // pp is const pointer to non-const pointer to T
      return *this;
    }

  private:
    T** pp;  // double indirection
  };

Of course, this is much less efficient, with all of the heap
manipulations and double-indirections, but I think it would work.
-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com



---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fenster@cs.columbia.edu (Sam Fenster)
Date: 1995/11/07
Raw View
> "ian (i.) willmott" <willmott@bnr.ca> writes:
>> It seems to me that this example tends to indicate that the prohibition of
>> non-const references to rvalues is a bad idea, at least for function
>> parameters. Section [basic.lval] suggests that you can in fact call a
>> non-const member function on a temporary object, so it's also inconsistent
> .
>> Why distinguish between member and non-member functions in this respect?
>
>> Surely there is a whole class of situations like this where the destructor
>> for a temporary has side effects which you may want to avoid or change by
>> modifying the object before it goes out of scope. I'm not sure what the ARM
>> means by "a major source of surprises"; if you pass a temporary as a
>> reference parameter, it's obviously going to evaporate after the function
>> returns, no matter what the function does or doesn't do to it.

kanze <kanze@lts.sel.alcatel.de> writes:
> The surprise occurs when you `accidentally' pass a temporary, such as the
> result of an implicit conversion.  Consider the following:
>  void incr (int& i) {i ++;}
>  unsigned j (0);
>  incr (j);                   //  j unchanged!!!
>
> I suppose the best rule would be to simply ban implicit conversions, and
> make the programmer say what he means.  But I rather suspect that the amount
> of code that this would break will prohibit any such change.

There is a perfect solution which I would be happy if someone proposed
officially.  It prevents the above pitfall, yet it allows temporaries to be
modified.  Also, it breaks no code since (I think -- check me out on this) it
merely relaxes the rules:

- First of all, get rid of the worse-than-useless restriction that disallows
binding non-const references to temporaries.

- Second (and last), add a rule that says that there is no implicit conversion
of a temporary to a non-const reference.

The intent of the currently existing rule is to prevent code from mistakenly
modifying the result of an implicit conversion instead of modifying the
original object.  This pitfall is the only reason I know of for prohibiting
functions modifying temporaries.  Witness the fact that the prohibition does
not extend to temporaries invoking non-const member functions since implicit
conversions are not done to objects invoking member functions.

The above proposed changes will still prevent this pitfall, by preventing the
implicit conversion to a temporary rather than preventing the binding of a
temporary.  The new rule will allow temporaries to be modified, which is
important to many designs, including auto_ptr<>.  And these changes will not
break existing code, since they only disallow the implicit conversion when the
resulting binding would be disallowed under current rules.

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: jjb@watson.ibm.com (John Barton)
Date: 1995/11/07
Raw View
In article <9511030943.AA23251@lts.sel.alcatel.de>, James Kanze US/ESC 60/3/141
 #40763 <kanze@lts.sel.alcatel.de> writes:
|> [delete some stuff]
|>                                    ...   The whole point of auto_ptr
|> is that the object is put into an auto_ptr immediatly after creation,
|> and is in an auto_ptr at all times.  Any tricks involving removing it
|> from one auto_ptr, and later putting it in another, are just that:
|> tricks, and introduce an element of danger.  The purpose of auto_ptr
|> is precisely to eliminate this danger.

  Nicely put.  Strict ownership is fundamentally incompatible with
auto_ptr<T> as function return type.  The latter implies ownership
by the returned temporary: lifetime control by a temporary is craziness.
(I have tried to beat this game myself several times).

   The way to use auto_ptr<T> successfully is to limit it to client
code.  Service providers write:
   // Callers own the lifetime of returned object; pick it up in an auto_ptr.
   Foo* doWorkAndCreateAFoo();
Clients pick up the result in auto del pointers:
   auto_ptr<Foo> my_foo(doWorkAndCreateFoo());
Anyone who fails to read and heed the comment loses.

--
John.

John J. Barton        jjb@watson.ibm.com            (914)784-6645
 <http://www.research.ibm.com/xw-SoftwareTechnology>
H1-C13 IBM Watson Research Center P.O. Box 704 Hawthorne NY 10598
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1995/11/07
Raw View
In article <47l6im$qu1@watnews2.watson.ibm.com> jjb@watson.ibm.com
(John Barton) writes:

|> In article <9511030943.AA23251@lts.sel.alcatel.de>, James Kanze US/ESC 60/3/
141
|>  #40763 <kanze@lts.sel.alcatel.de> writes:
|> |> [delete some stuff]
|> |>                                    ...   The whole point of auto_ptr
|> |> is that the object is put into an auto_ptr immediatly after creation,
|> |> and is in an auto_ptr at all times.  Any tricks involving removing it
|> |> from one auto_ptr, and later putting it in another, are just that:
|> |> tricks, and introduce an element of danger.  The purpose of auto_ptr
|> |> is precisely to eliminate this danger.

|>   Nicely put.  Strict ownership is fundamentally incompatible with
|> auto_ptr<T> as function return type.  The latter implies ownership
|> by the returned temporary: lifetime control by a temporary is craziness.
|> (I have tried to beat this game myself several times).

Put that way, it does sound crazy.  But I do it regularly with my
RefCntPtr class.  The temporary serves as a bridge between a local in
the called function (which has just gone out of scope), and a local in
the caller (which has yet to be initialized).  I would be interested
in hearing of examples where you have failed `to beat this game'.
Your statement worries me that there is a defect waiting to happen in
my own code.

|>    The way to use auto_ptr<T> successfully is to limit it to client
|> code.  Service providers write:
|>    // Callers own the lifetime of returned object; pick it up in an auto_ptr
.
|>    Foo* doWorkAndCreateAFoo();
|> Clients pick up the result in auto del pointers:
|>    auto_ptr<Foo> my_foo(doWorkAndCreateFoo());
|> Anyone who fails to read and heed the comment loses.

This fails on two counts:

1. The actual service provider may be several layers removed.  In
particular, an intermediate function may be used to `garnish' the
new'ed object before returning it to the client.  And presumably,
garnishing it could cause an exception.

2. As Bill Gibbons has pointed out, there is a window of opertunity at
the end of the server function in which the object may be lost.  While
I think that most people would agree that it is not a good idea to let
exceptions escape from destructors, good code - at least good library
code - will be robust even in this case.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1995/11/08
Raw View
law@solution-frameworks.com wrote:
>In <47l6im$qu1@watnews2.watson.ibm.com>, jjb@watson.ibm.com (John Barton) write
>s:
>>  Nicely put.  Strict ownership is fundamentally incompatible with
>>auto_ptr<T> as function return type.  The latter implies ownership
>>by the returned temporary: lifetime control by a temporary is craziness.
>>(I have tried to beat this game myself several times).
>>
>>   The way to use auto_ptr<T> successfully is to limit it to client
>>code.  Service providers write:
>>   // Callers own the lifetime of returned object; pick it up in an auto_ptr.
>>   Foo* doWorkAndCreateAFoo();
>>Clients pick up the result in auto del pointers:
>>   auto_ptr<Foo> my_foo(doWorkAndCreateFoo());
>>Anyone who fails to read and heed the comment loses.
>>
>>--
>>John.
>
>I'm confused, John.  You say "nicely put" but he said that objects
>should be put into an auto_ptr<> immediately after creation.  Your code
>certainly doesn't do that.

   Yes it does. "doWorkAndCreateFoo()" is a factory function
and is used directly as the argument for "my_foo"s ctor.

>Your code is also a prescription for undeleted objects (despite the
>comment).

   Thats the point: the constraints on correct use are
indeed _prescribed_ (documented). Thats a heck of a lot better
than a lot of commercial libraries which talk all about
functionality and forget to mention ownership.

>What's crazy about returning an auto_ptr. aside from the
>unnecessary restriction that it can't be used to assign to or construct
>another auto_ptr directly?  And why is lifetime control by a "temporary"
>fundamentally different than lifetime control by some named object?

    Temporaries are rvalues and so non-const references cannot be
bound to them by non-member functions, so they cannot be
modified (without a const_cast<>), and so they cannot
transfer ownership (which is a destructive operation).

    A solution is to provide a member function:

    struct auto_ptr { ...
       auto_ptr<T> &ref() { return *this; }
       ......
    };

and now:

    auto_ptr<int> f();
    auto_ptr<int> p(f().ref());

This is a way of converting a non-const rvalue into
a non-const lvalue. It is invasive (requires a member function)
but it is safe, which a const_cast<> is not.

--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 email: maxtal@suphys.physics.oz.au
AUSTRALIA                      email: skaller@maxtal.com.au



---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1995/11/09
Raw View
John Max Skaller (maxtal@suphys.physics.su.oz.au) wrote:

|>     Temporaries are rvalues and so non-const references cannot be
|> bound to them by non-member functions, so they cannot be
|> modified (without a const_cast<>), and so they cannot
|> transfer ownership (which is a destructive operation).

|>     A solution is to provide a member function:

|>     struct auto_ptr { ...
|>        auto_ptr<T> &ref() { return *this; }
|>        ......
|>     };

|> and now:

|>     auto_ptr<int> f();
|>     auto_ptr<int> p(f().ref());

|> This is a way of converting a non-const rvalue into
|> a non-const lvalue. It is invasive (requires a member function)
|> but it is safe, which a const_cast<> is not.

Just curious: in what way is this any safer than using a const_cast in
the implementation of auto_ptr<T>::operator=()?
--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
              -- A la recherche d'une activit    dans une region francophone

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: Sean A Corfield <sean@corf.demon.co.uk>
Date: 1995/11/13
Raw View
In article <47sm8h$4eh@gabi.gabi-soft.fr>,
kanze@gabi-soft.fr (J. Kanze) wrote:

|> Just curious: in what way is this any safer than using a const_cast in
|> the implementation of auto_ptr<T>::operator=()?

In Tokyo last week, Bill Gibbons suggested using a proxy nested class to
alleviate this problem. It works by having auto_ptr constructable from the
proxy object and assignable from the proxy object then having auto_ptr
convert to the proxy. Something like this:

  template<typename T> class auto_ptr {
    struct proxy {
      auto_ptr* p;
      proxy(auto_ptr* ap) : p(ap) { }
    };
  public:
    auto_ptr(const proxy& prox) : p(prox.p->p) { prox.p->p = 0; }
    // etc
    operator proxy() { return proxy(this); }
    // etc
  private:
    T* p;
  };

  auto_ptr<X> f();
  auto_ptr<X> p(f());
  // equivalent to:
  auto_ptr<X> p(f().operator proxy());

Similar code changes could be done for operator=. The committee have not
decided exactly what to do about fixing auto_ptr yet but they intend to do
_something_ in Scotts Valley...probably just make the parameter of the copy
constructor const and force the implementation to work around it with a
cast (ugh!).

Sean A. Corfield
Object Consultancy Services -- voting member of X3J16
C++ - Beyond the ARM http://uptown.turnpike.net/~scorf/cplusext.html
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: vandevod@cs.rpi.edu (David Vandevoorde)
Date: 1995/11/01
Raw View
>>>>> "FH" == Fergus Henderson <fjh@munta.cs.mu.OZ.AU> writes:
[...]
FH> Yep.  With the current spec., you need to write
FH>  auto_ptr<double> p(f().release());

Indeed... and the more I look at it, the more I like it :-P

FH> and
FH>  p.reset(f().release());

...but here I'm not sure; I think I'd rather see

 p = f().release();

and do away with the get() member.

 Daveed

 [I agree. -fjh.]

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: vandevod@cs.rpi.edu (David Vandevoorde)
Date: 1995/11/01
Raw View
>>>>> "IW" ==   <ian> writes:
IW> Some recent comments in this newsgroup indicate that there is some
IW> problem with returning an auto_ptr from a function. I was not
IW> aware of this; the April 1995 working paper does not mention it
IW> and it is not obvious to me why there should be a problem other
IW> than the overhead of constructing and destroying a temporary
IW> object. Could somebody better informed than I am explain what the
IW> issue is? My apologies if this has been discussed before; I missed
IW> it.
[...]

AFAIK, the problem is not so much with returning an auto_ptr as
what can be done with the return value (not much). The signatures
of the copy constructors and operator='s (which take non-const
auto_ptr's) are incompatible with the rvalue character of function
return values.

Hence,

 auto_ptr<double> f();
 auto_ptr<double> p(f());  // Ah no, copy-ctor take
     // non-const reference :(
and similarly:
 auto_ptr<double> p;
 p = f(); // Trouble again...

 Daveed
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: "ian (i.) willmott" <willmott@bnr.ca>
Date: 1995/11/02
Raw View
matt@cyclops5.berkeley.edu (Matt Austern) wrote:
>If a function returns an auto_ptr as an rvalue, it can't be used as
>the argument to a function that expects a non-const reference.  There
>are ways to get around this, but they're all a bit clumsy.

So the problem is that rvalues cannot be passed as non-const reference
parameters? Can you call a non-const member function on an object rvalue?
(Other than the destructor, which presumably will be called implicitly.)
If so, isn't that an inconsistency, since member functions are passed an
implicit reference to the object they are called for?

What part, if any, of the following code is legal under the draft standard?

class X {
    int i;
public:
    X(int);
    X(X &);
    int get(void);
    void set(int);
};

X f(void);
void g1(X &);
void g2(const X &);

void h(int i)
{
    X x(f());     // 1 - illegal?

    f().set(i);   // 2 - legal?
    g1(f());      // 3 - illegal?
    g2(f());      // 4 - legal?
}

My Cfront v3.03 compiler (HP) calls line 3 an anachronism, but doesn't mind
the others. I couldn't tell from sections [basic.lval] and [dcl.init.ref]
of the April draft (maybe this has changed in the September version)
exactly what the rule is, but the ARM has this to say (p154):

   If the initializer for a reference to type T is an lvalue of
   type T ... the reference will refer to the initializer;
   otherwise, if and only if the reference is to a const an object
   of type T will be created and initialized with the initializer.
   ...
   The distinction between references to consts and references to
   non-consts is a major change from earlier definitions of C++.
   The distinction was made to eliminate a major source of errors
   and surprises. Earlier, all references could be initialized to
   refer to temporary objects ...

Matt Austern:
>I don't know of any simply way to modify the definition of auto_ptr in
>the standard to avoid this difficulty: it seems to follow from the
>semantics of strict ownership.

It seems to me that this example tends to indicate that the prohibition of
non-const references to rvalues is a bad idea, at least for function
parameters. Section [basic.lval] suggests that you can in fact call
a non-const member function on a temporary object, so it's also inconsistent.
Why distinguish between member and non-member functions in this respect?

Surely there is a whole class of situations like this where the destructor
for a temporary has side effects which you may want to avoid or change by
modifying the object before it goes out of scope. I'm not sure what the ARM
means by "a major source of surprises"; if you pass a temporary as a reference
parameter, it's obviously going to evaporate after the function returns,
no matter what the function does or doesn't do to it.

I may be missing something here, but this seems like a really bad idea to me.
The ARM says "Naturally, this feature will be faded out of implementations
slowly, to avoid breaking working code", which sounds like an admission that
significant (and error-free) use has been made of it up until now. Maybe this
rule is what needs modification, rather than the definition of auto_ptr<T>.

Ian Willmott
Bell-Northern Research
Ottawa, Canada
(613)-763-9688
willmott@bnr.ca
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: uunet!PROBLEM_WITH_INEWS_GATEWAY_FILE!somers@ncar.UCAR.EDU (Stein Somers)
Date: 1995/11/02
Raw View
Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
: Yep.  With the current spec., you need to write

:  auto_ptr<double> p(f().release());

: and

:  p.reset(f().release());

: instead.

This has been mentioned earlier, of course.  So I'd like to repost
this recent uncountered reply:

Eric Biederman (ebiederm@cse.unl.edu) wrote:
: There is a case where it doesn't work.
:
: The problem is that you are passing it as a standard pointer type.
: What happens if you were to run out of stack after the call to release
: and before the call to the constructor, or reset.  And an exception is
: thrown.
:
: If your program actualy catches that exception then a memory leak has
: occured. ( A rare case I will admit).
:
: There are probably more realistic examples is less mundane code.

Maybe when an object containing many auto_ptr's is returned, in which
case the auto_ptr's must be moved jointly?

: It must be in an object at all times TO BE SAFE!

Any remarks, please?


Stein Somers (somers@intec.rug.ac.be)
INTEC - IMEC/University of Gent, Belgium
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kanze <kanze@lts.sel.alcatel.de>
Date: 1995/11/02
Raw View
In article <479thu$9k4@bcarh8ab.bnr.ca> "ian (i.) willmott"
<willmott@bnr.ca> writes:

|> Matt Austern:
|> >I don't know of any simply way to modify the definition of auto_ptr in
|> >the standard to avoid this difficulty: it seems to follow from the
|> >semantics of strict ownership.

|> It seems to me that this example tends to indicate that the prohibition of
|> non-const references to rvalues is a bad idea, at least for function
|> parameters. Section [basic.lval] suggests that you can in fact call
|> a non-const member function on a temporary object, so it's also inconsistent
.
|> Why distinguish between member and non-member functions in this respect?

|> Surely there is a whole class of situations like this where the destructor
|> for a temporary has side effects which you may want to avoid or change by
|> modifying the object before it goes out of scope. I'm not sure what the ARM
|> means by "a major source of surprises"; if you pass a temporary as a referen
ce
|> parameter, it's obviously going to evaporate after the function returns,
|> no matter what the function does or doesn't do to it.

The surprise occurs when you `accidentally' pass a temporary, such as
the result of an implicit conversion.  Consider the following:

 void
 incr( int& i )
 {
     i ++ ;
 }

 unsigned        j( 0 ) ;
 incr( j ) ;                   //  j unchanged!!!

I suppose the best rule would be to simply ban implicit conversions,
and make the programmer say what he means.  But I rather suspect that
the amount of code that this would break will prohibit any such
change.
--

James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/11/03
Raw View
"ian (i.) willmott" <willmott@bnr.ca> writes:

>matt@cyclops5.berkeley.edu (Matt Austern) wrote:
>>If a function returns an auto_ptr as an rvalue, it can't be used as
>>the argument to a function that expects a non-const reference.  There
>>are ways to get around this, but they're all a bit clumsy.
>
>So the problem is that rvalues cannot be passed as non-const reference
>parameters?

That's right.

>Can you call a non-const member function on an object rvalue?

Yes.

>If so, isn't that an inconsistency, since member functions are passed an
>implicit reference to the object they are called for?

You got it ;-)

>What part, if any, of the following code is legal under the draft standard?
>
>class X {
>    int i;
>public:
>    X(int);
>    X(X &);
>    int get(void);
>    void set(int);
>};
>
>X f(void);
>void g1(X &);
>void g2(const X &);
>
>void h(int i)
>{
>    X x(f());     // 1 - illegal?
>
>    f().set(i);   // 2 - legal?
>    g1(f());      // 3 - illegal?
>    g2(f());      // 4 - legal?
>}

Yes, 1 and 3 are ill-formed ("illegal"), while 2 and 4 are fine.

>I couldn't tell from sections [basic.lval] and [dcl.init.ref]
>of the April draft (maybe this has changed in the September version)
>exactly what the rule is

Here's what the April draft says.

|   5.2.2  Function call                                       [expr.call]
|
| 3 When a function is called, each parameter (_dcl.fct_)  shall  be  ini-
|   tialized  (_dcl.init.ref_, _class.copy_, _class.ctor_) with its corre-
|   sponding argument.

As far as I can tell, this rule applies to copy constructors, which are
after all a special type of member function.  Since the argument
of a copy constructor is a reference, we next consult [dcl.init.ref]:

|   8.5.3  References                                       [dcl.init.ref]
|
| 5 A reference to type "cv1 T1" is initialized by an expression  of  type
|   "cv2 T2" as follows:
|
|   --If  the initializer expression is an lvalue ...
...
| 8
|   --Otherwise,  the  reference  shall  be  to  a non-volatile const type
|     (i.e., cv1 shall be const).

This seems pretty clear to me - for a non-const reference, the
initializer must be an lvalue.  (If there was any doubt as to whether
this rule is intended to apply to copy constructors, it should be
dispelled by footnote 10 in this section.)

>My Cfront v3.03 compiler (HP) calls line 3 an anachronism, but doesn't mind
>the others.

Yes, there are quite a few compilers that detect the error in line 3
but fail to detect the error in line 1.  But I think these compilers
are just buggy; I think the draft standard definitely makes line 1
ill-formed.

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1995/11/03
Raw View
"ian (i.) willmott" <willmott@bnr.ca> wrote:
>matt@cyclops5.berkeley.edu (Matt Austern) wrote:
>>If a function returns an auto_ptr as an rvalue, it can't be used as
>>the argument to a function that expects a non-const reference.  There
>>are ways to get around this, but they're all a bit clumsy.
>
>So the problem is that rvalues cannot be passed as non-const reference
>parameters? Can you call a non-const member function on an object rvalue?

    YES, if the rvalue is non-const..

>If so, isn't that an inconsistency, since member functions are passed an
>implicit reference to the object they are called for?


    In my personal opinion, YES.

>What part, if any, of the following code is legal under the draft standard?
>
>class X {
>    int i;
>public:
>    X(int);
>    X(X &);
>    int get(void);
>    void set(int);
>};
>
>X f(void);
>void g1(X &);
>void g2(const X &);
>
>void h(int i)
>{
>    X x(f());     // 1 - illegal?
>    f().set(i);   // 2 - legal?
>    g1(f());      // 3 - illegal?
>    g2(f());      // 4 - legal?
>}

    1 is illegal, can't bind a non-const ref to an rvalue,
       even a non-const one.
    2 is legal, you can call a non-const member function
      on a non-const rvalue
    3 is illegal for the same reason as 1
    4 is legal

4 out of 4 right :-)


A more subtle question is whether

     g1(const_cast<X&>(f()));

is legal, assuming g1 actually modifies its argument.
I think so -- all const objects can be
hardware write protected by the implementation,
but the rvalue f() involved here is non-const, so
it _is_ legal to modify it.

>It seems to me that this example tends to indicate that the prohibition of
>non-const references to rvalues is a bad idea, at least for function
>parameters. Section [basic.lval] suggests that you can in fact call
>a non-const member function on a temporary object, so it's also inconsistent.
>Why distinguish between member and non-member functions in this respect?

    The argument is that it catches errors where you accidentally
modify a temporary (to no effect). In fact, doing exactly this
is a useful idiom, especially since the lifetime of temporaries
is now fixed (it wasn't in the ARM): it saves creating dummy
arguments where you don't WANT modifications to persist in
some more permanent object.

   So there's a trade off I think ought to be a quality
of implementation issue.


--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 email: maxtal@suphys.physics.oz.au
AUSTRALIA                      email: skaller@maxtal.com.au




---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: eddy@clipper.robadome.com (eddy Gorsuch)
Date: 1995/11/03
Raw View
In article <47b1um$fvs@infoserv.rug.ac.be>,
Stein Somers <uunet!PROBLEM_WITH_INEWS_GATEWAY_FILE!somers@ncar.UCAR.EDU> wrote
:
[...]
>This has been mentioned earlier, of course.  So I'd like to repost
>this recent uncountered reply:
>
>Eric Biederman (ebiederm@cse.unl.edu) wrote:
>: The problem is that you are passing it as a standard pointer type.
>: What happens if you were to run out of stack after the call to release
>: and before the call to the constructor, or reset.  And an exception is
>: thrown.

Ummm, who throws an exception if you run out of stack? I thought that came
in as a signal (or other hardware error), not as a C++ exception.

By the same token, what happens if you allocate some storage, then run
out of stack space while trying to call the auto_ptr's constructor (or
reset())? This leaves the same memory leak. I don't think these cases are
(nor should they be) covered by the C++ standard. They are too machine/OS
dependant.

eddy
--
ed.dy \'ed-e-\ n [ME (Sc dial.) ydy, prob. fr. ON itha; akin to OHG ith-
   again], L et and 1a: a current of water or air running contrary to the main

   current; esp)X : a small whirlpool 1b: a substance moving similarly  2: a
   contrary or circular current  - eddy vb
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1995/11/03
Raw View
In article <9530713.8851@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU
(Fergus Henderson) writes:

|> "ian (i.) willmott" <willmott@bnr.ca> writes:

|> >What part, if any, of the following code is legal under the draft standard?

|> >
|> >class X {
|> >    int i;
|> >public:
|> >    X(int);
|> >    X(X &);
|> >    int get(void);
|> >    void set(int);
|> >};
|> >
|> >X f(void);
|> >void g1(X &);
|> >void g2(const X &);
|> >
|> >void h(int i)
|> >{
|> >    X x(f());     // 1 - illegal?
|> >
|> >    f().set(i);   // 2 - legal?
|> >    g1(f());      // 3 - illegal?
|> >    g2(f());      // 4 - legal?
|> >}

|> Yes, 1 and 3 are ill-formed ("illegal"), while 2 and 4 are fine.

|> >I couldn't tell from sections [basic.lval] and [dcl.init.ref]
|> >of the April draft (maybe this has changed in the September version)
|> >exactly what the rule is

    [...]
|> This seems pretty clear to me - for a non-const reference, the
|> initializer must be an lvalue.  (If there was any doubt as to whether
|> this rule is intended to apply to copy constructors, it should be
|> dispelled by footnote 10 in this section.)

|> >My Cfront v3.03 compiler (HP) calls line 3 an anachronism, but doesn't mind

|> >the others.

|> Yes, there are quite a few compilers that detect the error in line 3
|> but fail to detect the error in line 1.  But I think these compilers
|> are just buggy; I think the draft standard definitely makes line 1
|> ill-formed.

One question: in the compilers which do detect the error in line 3,
but not in line 1, what is the actual code generated.  I suspect it is
not so much a case of the compiler binding a temporary to a non-const
reference, but one of the compiler not accepting
auto_ptr<T>::auto_ptr( auto_ptr<T>& ) as a copy constructor (since it
cannot be called), and *generating* its own copy constructor (with a
const parameter).

If this is the case, not only do you not get an error in the case of
line 1, you also get wrong code; both the temporary and the declared
variable think that they own the object pointed to.  When the
temporary is destructed (at the end of the statement), it will delete
the object, but the declared variable will continue to point to it.

I don't believe that the ARM was completely clear on this point; such
compilers may simply be implementing their interpretation of the ARM.
I would suggest, however, that given the above senario, the
implementors put this update at the highest priority.
--

James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: ralphs@tesser.com (Ralph Shnelvar)
Date: 1995/11/04
Raw View
kanze <kanze@lts.sel.alcatel.de> wrote:

>
>The surprise occurs when you `accidentally' pass a temporary, such as
>the result of an implicit conversion.  Consider the following:
. . .
>
>I suppose the best rule would be to simply ban implicit conversions,
>and make the programmer say what he means.  But I rather suspect that
>the amount of code that this would break will prohibit any such
>change.

Yesterday, on compuserve I said almost exactly the same thing.

Let there be a #pragma or -something- that turns off this awful
creation of temporaries.

Ralph



---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: ebiederm@cse.unl.edu (Eric Biederman)
Date: 1995/11/05
Raw View
In article <47c7pe$722@eclipse.eng.sc.rolm.com> eddy@clipper.robadome.com
(eddy Gorsuch) writes:
   In article <47b1um$fvs@infoserv.rug.ac.be>,
   Stein Somers <uunet!PROBLEM_WITH_INEWS_GATEWAY_FILE!somers@ncar.UCAR.EDU>
   wrote:
   [...]
   >This has been mentioned earlier, of course.  So I'd like to repost
   >this recent uncountered reply:
   >
   >Eric Biederman (ebiederm@cse.unl.edu) wrote:
   >: The problem is that you are passing it as a standard pointer type.
   >: What happens if you were to run out of stack after the call to release
   >: and before the call to the constructor, or reset.  And an exception is
   >: thrown.
[ hiden cut ]

   Ummm, who throws an exception if you run out of stack? I thought that came
   in as a signal (or other hardware error), not as a C++ exception.

[note: the useful way would for the signal to throw an exception.  It
       is an error appropriate to an exception. ]
[note: who throws exceptions, _now_? ]

Sorry, that was a bad example so acknowledged in a part you cut out.
A more realistic example happens if a signal handler is called
asynchronously.  It just happens that that happened while the call to
the autoptr's constructor or member reset was being formed upon the
stack.  This ill timed call to the signal code then throws an
exception.  Oops memory leak!

On the other hand if the memory was continually in an autoptr it would
be _safe_ from this (assuming the internal code for autoptr assignment
was written carefully, to avoid these kinds of problems).

   By the same token, what happens if you allocate some storage, then run
   out of stack space while trying to call the auto_ptr's constructor (or
   reset())? This leaves the same memory leak. I don't think these cases are
   (nor should they be) covered by the C++ standard. They are too machine/OS
   dependant.
[ ... ]

But that's transfering a pointer into an autoptr, the pointer makes no
guarantees of safety in that case.  But the __autoptr__ does!

Returning the autoptr to a normal pointer violates all gaurantes of
safety.  Assuming that you will still have safety is bad. It's as bad
as assuming all objects can be aligned on any address, or the bit
pattern of static_cast<void *>(0) is 0, or etc.

Once your pointer is in autoptr it's _safe_.  While your pointer is in
autoptr it should remain _safe_.

The issue of running out of stack space is not what I was arguing
about.  Simply my thought of a synchronous example that could cause
problems.  A very unrealistic example.

Eric
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: law@solution-frameworks.com
Date: 1995/11/06
Raw View
In <47ec58$3t3@meg.tesser.com>, ralphs@tesser.com (Ralph Shnelvar) writes:
>
>kanze <kanze@lts.sel.alcatel.de> wrote:
>
>>The surprise occurs when you `accidentally' pass a temporary, such as
>>the result of an implicit conversion.  Consider the following:
>.. . .
>>
>>I suppose the best rule would be to simply ban implicit conversions,
>>and make the programmer say what he means.  But I rather suspect that
>>the amount of code that this would break will prohibit any such
>>change.
>
>Yesterday, on compuserve I said almost exactly the same thing.
>
>Let there be a #pragma or -something- that turns off this awful
>creation of temporaries.
>
>Ralph

Perhaps the *best* rule would be one that permits the initializer for a
non-const T& to be *any* object of type T, not just lvalues?  It seems
there's a clear distinction between these three cases:

    void incr( int& );

    int    ok();
    double notOk;
    double maybe();

    incr( ok()  );   // This is OK.
    incr( notOk );   // This isn't.
    incr( maybe() ); // Why not?

In the first case, the reference is clearly bound to the same object that
the programmer specified as the initializer.  In the second, the reference
is clearly bound to a different object.  In the third, it's a different
object but I don't think it much matters.

I would like to see a rule that prohibits only cases where the binding of
the reference requires the conversion from a non-temporary initializer
to a temporary object.  At the least, I think it should be OK if no
conversions are required.

In other words, is it possible the baby was thrown out with the bathwater
when the "notOK" problem was fixed way back when?  Or is there no way
(syntactically or otherwise) to loosen up the rules just a little?

Bill Law
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: Fergus Henderson <fjh@cs.mu.oz.au>
Date: 1995/11/06
Raw View
ebiederm@cse.unl.edu (Eric Biederman) writes:

>eddy@clipper.robadome.com (eddy Gorsuch) writes:
>   Ummm, who throws an exception if you run out of stack? I thought that came
>   in as a signal (or other hardware error), not as a C++ exception.

The C standard and the draft C++ standard do not specify what happens
if you run out of stack space.  It is in the realm of undefined behaviour.

>[note: the useful way would for the signal to throw an exception.  It
>       is an error appropriate to an exception. ]

The C standard and the draft C++ standard do not specify any asynchronous
signal-handling facilities.  The draft C++ standard does not specify what
happens if a signal handler throws an exception.

>A more realistic example happens if a signal handler is called
>asynchronously.  It just happens that that happened while the call to
>the autoptr's constructor or member reset was being formed upon the
>stack.  This ill timed call to the signal code then throws an
>exception.  Oops memory leak!
>
>On the other hand if the memory was continually in an autoptr it would
>be _safe_ from this (assuming the internal code for autoptr assignment
>was written carefully, to avoid these kinds of problems).

How can the internal code for autoptr assignment be written to
avoid these kinds of problems?

I think allowing asynchronous signal handlers to throw exceptions would
create formidable problems; solving them might require Herculean
feats.  I don't think the C++ standard can support such things, and I
don't think the auto_ptr class should try.  For anyone who is trying to
write safe code in an environment where exceptions may be thrown
asynchronously, auto_ptr will be the least of their problems.

For example, what happens if a signal handler throws an exception while
you were in the middle of throwing another exception?

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: "ian (i.) willmott" <willmott@bnr.ca>
Date: 1995/10/31
Raw View
Some recent comments in this newsgroup indicate that there is some problem with
returning an auto_ptr from a function. I was not aware of this; the April 1995
working paper does not mention it and it is not obvious to me why there should
be a problem other than the overhead of constructing and destroying a temporary
object. Could somebody better informed than I am explain what the issue is? My
apologies if this has been discussed before; I missed it.

Also, could somebody post the procedure for submitting comments on the draft
standard? I sent email to 'c++std-notify@research.att.com' in July and got back
a receipt acknowledgement, but nothing else ever came. Again, I expect that
this has been posted before, but I didn't see it. Thanks.

Ian Willmott
Bell-Northern Research
Ottawa, Canada
(613)-763-9688
willmott@bnr.ca


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/11/01
Raw View
vandevod@cs.rpi.edu (David Vandevoorde) writes:

>AFAIK, the problem is not so much with returning an auto_ptr as
>what can be done with the return value (not much). The signatures
>of the copy constructors and operator='s (which take non-const
>auto_ptr's) are incompatible with the rvalue character of function
>return values.
>
>Hence,
>
> auto_ptr<double> f();
> auto_ptr<double> p(f());  // Ah no, copy-ctor take
>     // non-const reference :(
>and similarly:
> auto_ptr<double> p;
> p = f(); // Trouble again...

Yep.  With the current spec., you need to write

 auto_ptr<double> p(f().release());

and

 p.reset(f().release());

instead.

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]