Topic: Binding Temporaries


Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 2000/06/14
Raw View
In article <20000613.17053772@jb-11116.stardiv.de>, J=F6rg Barfurth
<joerg.barfurth@attglobal.net> writes
>
>Am 13.06.00, 23:41:53, schrieb Francis Glassborow=20
><francis@robinton.demon.co.uk> zum Thema Re: Binding Temporaries:
>
>
>> In article <AG815.1120$Qf6.45090@nuq-read.news.verio.net>, C. M. Heard
>> <heard@vvnet.com> writes
>> >I'd rather live with copying (i.e., be required to use pass by value)
>
>> You might but I think you would be in a very small minority. Among oth=
er
>> things you now have to have a publicly accessible copy ctor for all su=
ch
>> cases.
>
>But you do need to have a publicly accessible copy ctor anyway, if you=20
>want to initialize a reference from an rvalue.

Maybe it is because I am tired but I do not understand this.  What is
wrong with:

class X {
        int i;
        X(X const &);
        X& operator=3D(X const &);
public:
        X(int i_ =3D 0):i(i_){}
};

int main(){
        X const & x=3D 3;
        X const & x1 =3D *new X;
        delete &x1;
}


Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

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






Author: Biju Thomas <b.thomas@attglobal.net>
Date: 2000/06/15
Raw View
"C. M. Heard" wrote:
>
> Yes, if I want value semantics then for for built-in types passing by value
> is always preferable to passing by const reference.  But ...
>
> > So, what about following a guideline that "never pass builtin types by
> > const reference"? I think it will solve your problem.
>
> No, it will not.  Consider this:
>
> class A {
>   private:
>     const unsigned int& external_data;
>   public:
>     /* ... */
>   A (const unsigned int& ext_data) : external_data(ext_data) { }
> };
>
> That's not safe because of the possibility of an implicit conversion and
> the possibility of binding to a temporary.

Agreed. But, my question was, why do you need to do the above for
builtin types? Why can't you just use a simple 'const unsigned int'
instead of the reference? Do the reference buy you anything extra?

--
Biju Thomas

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






Author: "C. M. Heard" <heard@vvnet.com>
Date: 2000/06/15
Raw View
> > > So, what about following a guideline that "never pass builtin types by
> > > const reference"? I think it will solve your problem.
> >
> > No, it will not.  Consider this:
> >
> > class A {
> >   private:
> >     const unsigned int& external_data;
> >   public:
> >     /* ... */
> >   A (const unsigned int& ext_data) : external_data(ext_data) { }
> > };
> >
> > That's not safe because of the possibility of an implicit conversion and
> > the possibility of binding to a temporary.
>
> Agreed. But, my question was, why do you need to do the above for
> builtin types? Why can't you just use a simple 'const unsigned int'
> instead of the reference? Do the reference buy you anything extra?

It's essential:  the variable that the reference is bound to is modified
outside the class.  The purpose of the class, in fact, is to allow the
value of the external variable to be read remotely, via a network management
protocol.

Mike

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






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/06/16
Raw View
Am 15.06.00, 01:15:07, schrieb Francis Glassborow=20
<francis@robinton.demon.co.uk> zum Thema Re: Binding Temporaries:


> In article <20000613.17053772@jb-11116.stardiv.de>, J=F6rg Barfurth
> <joerg.barfurth@attglobal.net> writes

> >But you do need to have a publicly accessible copy ctor anyway, if you
> >want to initialize a reference from an rvalue.

> Maybe it is because I am tired but I do not understand this.  What is
> wrong with:

> class X {
>         int i;
>         X(X const &);
>         X& operator=3D(X const &);
> public:
>         X(int i_ =3D 0):i(i_){}
> };

> int main(){
>         X const & x=3D 3;
>         X const & x1 =3D *new X;
>         delete &x1;
> }

It (the first line of main) is ill-formed.

The extensive quote I appended below is from the draft available at=20
http://anubis.dkuug.dk/jtc1/sc22/open/n2356/, but this essentially didn't=
=20
change in the final standard (except, perhaps, for paragraph numbers).

The text describes initialization of references.

The paragraphs numbered 8 and 9 in the quotation state that:
    If  the initializer expression is an rvalue [of a] class  type=20
    [with which the reference is reference-compatible], ...=20
    the  appropriate  copy constructor must be callable whether or not
    the copy is actually done.

Paragraph 10 in the quotation states that:
    --Otherwise, a temporary [...] is created and initialized
      from  the  initializer expression using the rules for a non-refer-
      ence copy initialization ....

Thus in both cases the copy constructor must be callable. Your example is=
=20
covered by paragraph 10.

Expanding on that example:

class X {
        int i;
        X(X const &);
        X& operator=3D(X const &);
public:
        X(int i_ =3D 0):i(i_){}
        int data() const { return i; }
};

class Y : public X {
public:
        Y(int i_ =3D 0):X(i_){}
        Y(Y const & y_):X(y_.data()) {}
};

extern X sourceX();
extern Y sourceY();

int main(){
        Y const & y  =3D 3;         // OK: Conversion + Copy ctor=20
accessible
        Y const & y1 =3D *new X;    // OK: Bind directly to lvalue
        Y const & y2 =3D Y(3);      // OK: Copy ctor accessible
        Y const & y3 =3D sourceY(); // OK: Copy ctor accessible

        X const & x  =3D 3;         // Error: Copy ctor inaccessible
        X const & x1 =3D *new X;   =20
        X const & x2 =3D X(3);      // Error: Copy ctor inaccessible
        X const & x3 =3D sourceX(); // Error: Copy ctor inaccessible

 // the standard says '[sic!]'
        X const & z1 =3D *new Y;    // OK: Bind directly to lvalue
        X const & z2 =3D Y(3);      // OK: Copy ctor (of Y !) accessible
        X const & z3 =3D sourceY(); // OK: Copy ctor (of Y !) accessible

        delete &x1;
        delete &y1;
        delete &z1;
}

Regards, J=F6rg

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -=
=20
-=20
<QUOTE skipped=3D"[...]">
8.5.3 [dcl.init.ref]=20
[...]
=20
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 (but not an lvalue for a
    bit-field), and

6
    --"cv1 T1" is reference-compatible with "cv2 T2," or

    --T2 is  a  class  type,  and  the  initializer  expression  can  be

      implicitly converted to an lvalue of type "cv3 T3," where "cv1 T1"
      is  reference-compatible  with  "cv3 T3"  9)  (this  conversion is
      selected  by  enumerating  the  applicable  conversion   functions
      (_over.match.ref_) and choosing the best one through overload res-
      olution (_over.match_)), then
[...]

8
  --Otherwise,  the  reference  shall  be  to  a non-volatile const type
    (i.e., cv1 shall be const).  [Example:
[...]
     --end example]

    --If the initializer expression is an rvalue, with T2 a class  type,
      and  "cv1 T1" is reference-compatible with "cv2 T2," the reference
      is bound in one of the following ways (the choice  is  implementa-
      tion-defined):

      --The reference is bound directly to the object represented by the
        rvalue (see _basic.lval_) or to a sub-object within that object.

      --A  temporary  of type "cv1 T2" [sic] is created, and a copy con-
        structor is called to copy the entire  rvalue  object  into  the
        temporary.  The reference is bound to the temporary or to a sub-
        object within the temporary.10)
=20
9     The  appropriate  copy constructor must be callable whether or not
      the copy is actually done.  [Example:
                  struct A { };
                  struct B : public A { } b;
                  extern B f();
                  const A& rca =3D f();     // Either bound directly or
                                          //   the entire B object is=20
copied and
                                          //   the reference is bound to=20
the
                                          //   A sub-object of the copy
       --end example]

10
    --Otherwise, a temporary of type "cv1 T1" is created and initialized
      from  the  initializer expression using the rules for a non-refer-
      ence copy initialization  (_dcl.init_).   The  reference  is  then
      bound  to  the  temporary.   If T1 is reference-related to T2, cv1
      must be the same cv-qualification as, or greater  cv-qualification
      than, cv2; otherwise, the program is ill-formed.  [Example:
                  const double& rcd2 =3D 2; // rcd2 refers to temporary
                                          // with value `2.0'
                  const volatile int cvi =3D 1;
                  const int& r =3D cvi;     // error: type qualifiers=20
dropped
       --end example]
</QUOTE>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -=
=20
-=20
--
J=F6rg Barfurth                   j barfurth AT vossnet DOT de
-------------- using std::disclaimer; ----------------------
Software Engineer               Tel: +49 40 23646 500
Star Office GmbH                Fax: +49 40 23646 550
Sachsenfeld 4                   mailto:joerg.barfurth@germany.sun.com
D-20097 Hamburg                 http://www.sun.com/staroffice



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






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 2000/06/16
Raw View
In article <20000615.12525607@jb-11116.stardiv.de>, J=F6rg Barfurth
<joerg.barfurth@attglobal.net> writes
>Thus in both cases the copy constructor must be callable. Your example i=
s=20
>covered by paragraph 10.
>
>Expanding on that example:
>
>class X {
>        int i;
>        X(X const &);
>        X& operator=3D(X const &);
>public:
>        X(int i_ =3D 0):i(i_){}
>        int data() const { return i; }
>};
>
>class Y : public X {
>public:
>        Y(int i_ =3D 0):X(i_){}
>        Y(Y const & y_):X(y_.data()) {}
>};
>
>extern X sourceX();
>extern Y sourceY();
>
>int main(){
>        Y const & y  =3D 3;         // OK: Conversion + Copy ctor=20
>accessible
>        Y const & y1 =3D *new X;    // OK: Bind directly to lvalue
>        Y const & y2 =3D Y(3);      // OK: Copy ctor accessible
>        Y const & y3 =3D sourceY(); // OK: Copy ctor accessible
>
>        X const & x  =3D 3;         // Error: Copy ctor inaccessible
>        X const & x1 =3D *new X;   =20
>        X const & x2 =3D X(3);      // Error: Copy ctor inaccessible
>        X const & x3 =3D sourceX(); // Error: Copy ctor inaccessible
>
>       // the standard says '[sic!]'
>        X const & z1 =3D *new Y;    // OK: Bind directly to lvalue
>        X const & z2 =3D Y(3);      // OK: Copy ctor (of Y !) accessible
>        X const & z3 =3D sourceY(); // OK: Copy ctor (of Y !) accessible
>
>        delete &x1;
>        delete &y1;
>        delete &z1;
>}
>
>Regards, J=F6rg

Thank you for the detailed correction. I think we have an over
restricting requirement.  For example:

int const & i =3D 3.14159;

is well formed. I cannot imagine any circumstance in which requiring
that copy ctor be accessible for binding a temporary to const ref makes
any sense.  Perhaps that is a lack of imagination by me.  I think we
should consider relaxing this requirement in the next version of C++.



Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

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






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/06/16
Raw View
Am 16.06.00, 05:52:11, schrieb Francis Glassborow=20
<francis@robinton.demon.co.uk> zum Thema Re: Binding Temporaries:


> In article <20000615.12525607@jb-11116.stardiv.de>, J=F6rg Barfurth
> <joerg.barfurth@attglobal.net> writes
> >Thus in both cases the copy constructor must be callable. Your example=
=20
is
> >covered by paragraph 10.

> Thank you for the detailed correction. I think we have an over
> restricting requirement.  For example:

> int const & i =3D 3.14159;

> is well formed.=20

Sure. In that case (again what I labeled p.10) the standard did not=20
require a copy ctor, but copy-initializing the temporary (lvalue) that=20
would be bound to the reference. You can copy-initialize a 'const int'=20
from a 'double' rvalue, so there is no problem.

> I cannot imagine any circumstance in which requiring
> that copy ctor be accessible for binding a temporary to const ref makes
> any sense.  Perhaps that is a lack of imagination by me.  I think we
> should consider relaxing this requirement in the next version of C++.

I think the intent was to support implementations that really do treat=20
rvalues differently from lvalues.
As long as temporaries are treated like rvalues, it is hard to make a=20
distinction here:

For implementations that store rvalues of small class types in something=20
like a register, it may make sense that in

    struct Small { /*...*/ };
    Small source(); // rvalue returned in register
    void foo(Small const*);

    Small const& ref =3D source();
    Small const& ref2 =3D Small();
    foo(&ref);
   =20
... the reference initializations needs to copy the rvalue to real memory=
=20
which allows addresses to be taken.

After all

    foo(&source());=20
    foo(&Small());=20

is illegal for a reason, isn't it ?

-- J=F6rg Barfurth


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






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 2000/06/16
Raw View
In article <20000615.18424245@jb-11116.stardiv.de>, J=F6rg Barfurth
<joerg.barfurth@attglobal.net> writes
>I think the intent was to support implementations that really do treat=20
>rvalues differently from lvalues.
>As long as temporaries are treated like rvalues, it is hard to make a=20
>distinction here:

But we already allow so many other things to be done to rvalues for UDTs
that it hardly seems consistent. Note that in order to call a copy ctor
you must be able to bind something to the parameter of the ctor.

Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

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






Author: James Dennett <james@jamesd.demon.co.uk>
Date: 2000/06/10
Raw View
Andre Kostur wrote:

> I'm kinda curious as to what the rationale was behind disallowing the
> binding of temporaries to non-const references?

In a nutshell, the only reason to bind an object to a non-const reference
is to modify the original object.  If the original object is a temporary,
this
modification would be lost -- probably a coding error on the part of the
caller.

For example, if we defined "void increment(int &r) { ++ r; }" and it was
called with an argument of type char then the char would be promoted to
a temporary int, and the temporary modified.

While it would sometimes be convenient to be able to call a method which
modifies a reference parameter for its side-effects by passing a temporary,
that usually indicates a badly designed interface.  The advantage of spotting

erroneous calls makes up for any possible lost convenience, IMO.

-- James Dennett <jdennett@acm.org>


[snip]

>
> What danger are we avoiding by disallowing this sort of thing?

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






Author: "C. M. Heard" <heard@vvnet.com>
Date: 2000/06/13
Raw View
"Andrei Alexandrescu" <andrewalex@hotmail.com> wrote:
> James Dennett <james@jamesd.demon.co.uk> wrote in message
> news:393E9F42.4CF4020@jamesd.demon.co.uk...
> > While it would sometimes be convenient to be able to call a method which
> > modifies a reference parameter for its side-effects by passing a temporary,
> > that usually indicates a badly designed interface.  The advantage of
> > spotting erroneous calls makes up for any possible lost convenience, IMO.
>
> Hmmm, I think it's not that clear. I guess binding temporaries to
> non=const references should be banned only when an implicit conversion
> is involved. Consider this:
>
> string s;
> extern string GetUser();
> s.swap(GetUser()); // error!
> GetUser().swap(s); // ok
>
> This asymmetry is annoying. I think the rule should be relaxed.

I agree that the asymmetry is annoying, but the remedy I'd like to see
is not a relaxation but an outright ban on binding references of any
kind (const or non-const) to temporary objects.

In my not very humble opinion it would be much better for const references
to be aliases for real (non-temporary) objects, just as non-const references
are, but with the proviso that they are not modifiable.  As things now
stand a const reference parameter has nearly the same semantics as a value
parameter.  There are certain situations where one wants an alias for a
non-temporary object which will not be modified -- e.g., for a constructor
parameter to initialise a const reference member.  The possibility of an
implicit conversion and binding to a temporary make const references unsafe
for this purpose.  One is forced either to use a non-const reference (not
always possible, if the object one is binding to is const) or a pointer to
const.  Either of these misstates the interface contract (one by dropping
const, the other by admitting the possibility of a null pointer).

Mike

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






Author: qrczak@knm.org.pl (Marcin 'Qrczak' Kowalczyk)
Date: 2000/06/13
Raw View
Tue, 13 Jun 2000 01:34:46 CST, C. M. Heard <heard@vvnet.com> pisze:

> I agree that the asymmetry is annoying, but the remedy I'd like to see
> is not a relaxation but an outright ban on binding references of any
> kind (const or non-const) to temporary objects.

How would you write a function that accepts any expression of a given
type as the argument, but in case a non-temporary is passed, no copy
of it is created?

--
 __("<    Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/
 \__/              GCS/M d- s+:-- a23 C+++$ UL++>++++$ P+++ L++>++++$ E-
  ^^                  W++ N+++ o? K? w(---) O? M- V? PS-- PE++ Y? PGP+ t
QRCZAK                  5? X- R tv-- b+>++ DI D- G+ e>++++ h! r--%>++ y-

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






Author: Andre@incognito.com (Andre Kostur)
Date: 2000/06/13
Raw View
james@jamesd.demon.co.uk (James Dennett) wrote in
<393E9F42.4CF4020@jamesd.demon.co.uk>:

>Andre Kostur wrote:
>
>> I'm kinda curious as to what the rationale was behind disallowing the
>> binding of temporaries to non-const references?
>
>In a nutshell, the only reason to bind an object to a non-const
>reference is to modify the original object.  If the original object is a
>temporary, this
>modification would be lost -- probably a coding error on the part of the
>caller.
>
>For example, if we defined "void increment(int &r) { ++ r; }" and it was
>called with an argument of type char then the char would be promoted to
>a temporary int, and the temporary modified.
>
>While it would sometimes be convenient to be able to call a method which
>modifies a reference parameter for its side-effects by passing a
>temporary, that usually indicates a badly designed interface.  The
>advantage of spotting
>
>erroneous calls makes up for any possible lost convenience, IMO.
>
>-- James Dennett <jdennett@acm.org>

OK, what about the case where the construction and destruction of the
object itself has side effects in the constructor and destructor (such as
the acquision and release of a mutex/critical section/condition variable) ?

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






Author: "C. M. Heard" <heard@vvnet.com>
Date: 2000/06/13
Raw View
Marcin 'Qrczak' Kowalczyk wrote:
> > I agree that the asymmetry is annoying, but the remedy I'd like to see
> > is not a relaxation but an outright ban on binding references of any
> > kind (const or non-const) to temporary objects.
>
> How would you write a function that accepts any expression of a given
> type as the argument, but in case a non-temporary is passed, no copy
> of it is created?

I'd rather live with copying (i.e., be required to use pass by value) in this
case than have no means to express the requirement that a parameter (a) shall
refer to a non-temporary object of a given type, (b) shall not be used to
modify that object, and (c) shall not be a null pointer.  Currently the
language has no means to express these semantics.  The closest that I can
get is (a) and (b), from a pointer to const, or (a) and (c), from a non-const
reference.  As things now stand a value parameter and a const reference
parameter have essentially the same semantics, except that a const reference
requires the copy to be elided when it is not needed.  The cost of this
optimization is loss of expressive power.

Mike

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






Author: James Dennett <james@jamesd.demon.co.uk>
Date: 2000/06/13
Raw View
Gabriel Dos Reis wrote:

> James Dennett <james@jamesd.demon.co.uk> writes:
>
> | Andre Kostur wrote:
> |
> | > I'm kinda curious as to what the rationale was behind disallowing the
> | > binding of temporaries to non-const references?
> |
> | In a nutshell, the only reason to bind an object to a non-const reference
> | is to modify the original object.  If the original object is a temporary,
> | this
> | modification would be lost -- probably a coding error on the part of the
> | caller.
>
> Hmm, then how do you explain the ability of calling non-const member
> functions for temporary objects?
>

It seems to be somewhat inconsistent, doesn't it?

Some idioms (such as "container().swap(instance_to_empty)")
certainly do create temporaries and modify them as essential
side-effects.  I wasn't on the C++ Committee, and can't say
whether the similarity between X().swap(y) and y.swap(X())
was discussed when banning one but not the other.  They seem
to be equally safe (or unsafe) to me.

-- James.


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






Author: James Dennett <james@jamesd.demon.co.uk>
Date: 2000/06/13
Raw View
Kevin Kostrzewa wrote:

> James Dennett <james@jamesd.demon.co.uk> writes:
> > In a nutshell, the only reason to bind an object to a non-const reference
> > is to modify the original object.  If the original object is a temporary,
> > this
> > modification would be lost -- probably a coding error on the part of the
> > caller.
>
> What about the case of using the temporary unnamed object in an
> expression, though?  For example:
>
> class Foo
> {
> public:
>         Foo &modify() { /* do something */ return *this; }
> };
>
> void bar(const Foo &);
>
> bar(Foo().modify());
>

That brings in a related loophole, for anyone who needs it:

class Foo
{
public:
    // Callable on a temporary (which won't bind to a
    // non-const reference), this returns a reference to
    // the same temporary which will.
    Foo & reference_from_temporary() { return *this; /* do nothing */ }
};

void bar(Foo &) { }

bar(Foo().reference_from_temporary());

(maybe a shorter name for reference_from_temporary would help).
Including such a method in any class which needs it is clearly only
an option for new classes (or at least code whose source code can
be modified).

Possible Andrei's suggestion (allow binding to non-const reference
for explicitly defined temporaries but not for compiler-generated
temporaries) catches most of the accidents while allowing clear
code to be written.

-- James Dennett <jdennett@acm.org>


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






Author: qrczak@knm.org.pl (Marcin 'Qrczak' Kowalczyk)
Date: 2000/06/13
Raw View
Tue, 13 Jun 2000 03:08:07 CST, C. M. Heard <heard@vvnet.com> pisze:

> I'd rather live with copying (i.e., be required to use pass by value)
> in this case

This would be very bad.

Mandatory copying of such values is nonsense. Values can be huge
trees. So functions would use references to const anyway, and instead
arguments would have to be bound to explicit variables instead of
being passed directly.

> than have no means to express the requirement that a parameter (a)
> shall refer to a non-temporary object of a given type, (b) shall not
> be used to modify that object, and (c) shall not be a null pointer.

IMHO the former is much more important than this. Something similar to
current passing by reference to const is about the only way of passing
parameters in most languages (e.g. all functional languages); other
languages have non-const values at all and allow binding non-const
references to temporaries (e.g. most OO languages).

I lied, in fact these languages don't work that way. They have
garbage collection and return values by pointers. C++ does not have
GC, so usually the closest thing possible is to return values by
temporaries, so they need not to be explicitly freed, with the cost
of some unneeded copying. There is a difference in return values,
but there is no need to introduce similar difference in parameters
and require copying of them.

It is essential to be able to pass both variables and function results
to the same function.

--
 __("<    Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/
 \__/              GCS/M d- s+:-- a23 C+++$ UL++>++++$ P+++ L++>++++$ E-
  ^^                  W++ N+++ o? K? w(---) O? M- V? PS-- PE++ Y? PGP+ t
QRCZAK                  5? X- R tv-- b+>++ DI D- G+ e>++++ h! r--%>++ y-

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






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 2000/06/13
Raw View
In article <AG815.1120$Qf6.45090@nuq-read.news.verio.net>, C. M. Heard
<heard@vvnet.com> writes
>I'd rather live with copying (i.e., be required to use pass by value)

You might but I think you would be in a very small minority. Among other
things you now have to have a publicly accessible copy ctor for all such
cases.

Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

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






Author: "C. M. Heard" <heard@vvnet.com>
Date: 2000/06/13
Raw View
Marcin 'Qrczak' Kowalczyk wrote:
> Mandatory copying of such values is nonsense. Values can be huge
> trees. So functions would use references to const anyway, and instead
> arguments would have to be bound to explicit variables instead of
> being passed directly.

Yes, precisely, if binding of a const reference parameter to a temporary
were not allowed then any value _that required a type conversion_ would
have to be bound to an explicit variable, i.e., copied explicitly.  This
would not change the rules about when a copy has to be made.   But it
would prevent expensive copy operations from being done silently and
with no warning from the compiler.

Mike

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






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 2000/06/13
Raw View
In article <eg715.1115$Qf6.45319@nuq-read.news.verio.net>, C. M. Heard
<heard@vvnet.com> writes
>
>I agree that the asymmetry is annoying, but the remedy I'd like to see
>is not a relaxation but an outright ban on binding references of any
>kind (const or non-const) to temporary objects.

That would be very difficult because that means that it would be
impossible for function parameters to bind to either literals or to
conversions.

Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

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






Author: Biju Thomas <b.thomas@attglobal.net>
Date: 2000/06/13
Raw View
"C. M. Heard" wrote:
>
> In my not very humble opinion it would be much better for const references
> to be aliases for real (non-temporary) objects, just as non-const references
> are, but with the proviso that they are not modifiable.  As things now
> stand a const reference parameter has nearly the same semantics as a value
> parameter.  There are certain situations where one wants an alias for a
> non-temporary object which will not be modified -- e.g., for a constructor
> parameter to initialise a const reference member.  The possibility of an
> implicit conversion and binding to a temporary make const references unsafe
> for this purpose.

I am curious to know when this happens for user-defined types. To the
best of my knowledege, it will be a problem only if you provide such
conversion operators for your class.

I agree that it is a problem for builtin types where there are implicit
conversions to other builtin types. But, for bultin types, I don't see
any problem in passing objects by value if that is what you need.

So, what about following a guideline that "never pass builtin types by
const reference"? I think it will solve your problem.

> One is forced either to use a non-const reference (not
> always possible, if the object one is binding to is const) or a pointer to
> const.  Either of these misstates the interface contract (one by dropping
> const, the other by admitting the possibility of a null pointer).

--
Biju Thomas

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






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 2000/06/14
Raw View
James Dennett wrote:
>
> Andre Kostur wrote:
>
> > I'm kinda curious as to what the rationale was behind disallowing the
> > binding of temporaries to non-const references?
>
> In a nutshell, the only reason to bind an object to a non-const reference
> is to modify the original object.  If the original object is a temporary,
> this
> modification would be lost -- probably a coding error on the part of the
> caller.
>
> For example, if we defined "void increment(int &r) { ++ r; }" and it was
> called with an argument of type char then the char would be promoted to
> a temporary int, and the temporary modified.
>
> While it would sometimes be convenient to be able to call a method which
> modifies a reference parameter for its side-effects by passing a temporary,
> that usually indicates a badly designed interface.  The advantage of spotting
>
> erroneous calls makes up for any possible lost convenience, IMO.

But there are cases where an object is just "passed through"
a function. The best example for this is stream output through
operator<<. Wouldn't it make sense to write:

std::string s = (std::ostringstream() << foo << bar << baz).str();

This code has two problems. The first one is that it tries to
bind a temporary to a non-const reference. In this case, it
_is_ wanted, because the modification of the temporary is
exactly what we want.
There's of course a second problem in this code, which is
that operator<< returns ostream&, so the information that we
indeed have an ostringstream got lost. Especially, you cannot
call str() on it.

It would be nice to have a pass-through mechanism, say

pass operator<<(pass ostream& o, Foo const& foo)
{
  o << foo.a << foo.b; // or whatever
}

The "pass" return value would be handled like a void
return value inside the function, and on use would direct
the compiler to use the _original_ object as return value
(esp. with the exact type). Since that object will still
be accessible, it's clear that modifying it is not an error.
Implicit conversions on the argument should be disallowed,
though.

The fact that the original object is used as "return value"
would also allow binding that to a const reference afterwards
_without_ loosing the lifetime extension:

struct Foo
{
  Foo& operator++() pass { ++i; return *this; }
    // as usual, appended modifiers apply to *this
  int i;
};

struct Bar
{
  pass operator++() pass { ++i; }
  int i;
};

void foobar()
{
  Foo const& foo = ++Foo();
    // temporary will dissappear here, leaving a dangling reference

  Bar const& bar = ++Bar();
    // temporary will exist until end of function

  // ...
}

For functions defined in other translation units, the compiler
might also be able to optimize better through the knowledge
that the returned object is just the argument object (f.ex.
in aliasing analysis).

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






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/06/14
Raw View
Am 13.06.00, 23:41:53, schrieb Francis Glassborow=20
<francis@robinton.demon.co.uk> zum Thema Re: Binding Temporaries:


> In article <AG815.1120$Qf6.45090@nuq-read.news.verio.net>, C. M. Heard
> <heard@vvnet.com> writes
> >I'd rather live with copying (i.e., be required to use pass by value)

> You might but I think you would be in a very small minority. Among othe=
r
> things you now have to have a publicly accessible copy ctor for all suc=
h
> cases.

But you do need to have a publicly accessible copy ctor anyway, if you=20
want to initialize a reference from an rvalue.

-- J=F6rg Barfurth


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






Author: "C. M. Heard" <heard@vvnet.com>
Date: 2000/06/14
Raw View
Biju Thomas wrote:
> "C. M. Heard" wrote:
>
> > In my not very humble opinion it would be much better for const references
> > to be aliases for real (non-temporary) objects, just as non-const references
> > are, but with the proviso that they are not modifiable.  As things now
> > stand a const reference parameter has nearly the same semantics as a value
> > parameter.  There are certain situations where one wants an alias for a
> > non-temporary object which will not be modified -- e.g., for a constructor
> > parameter to initialise a const reference member.  The possibility of an
> > implicit conversion and binding to a temporary make const references unsafe
> > for this purpose.
>
> I am curious to know when this happens for user-defined types. To the
> best of my knowledege, it will be a problem only if you provide such
> conversion operators for your class.

That is correct.

> I agree that it is a problem for builtin types where there are implicit
> conversions to other builtin types. But, for bultin types, I don't see
> any problem in passing objects by value if that is what you need.

Yes, if I want value semantics then for for built-in types passing by value
is always preferable to passing by const reference.  But ...

> So, what about following a guideline that "never pass builtin types by
> const reference"? I think it will solve your problem.

No, it will not.  Consider this:

class A {
  private:
    const unsigned int& external_data;
  public:
    /* ... */
  A (const unsigned int& ext_data) : external_data(ext_data) { }
};

That's not safe because of the possibility of an implicit conversion and
the possibility of binding to a temporary.  One alternative is

class A {
  private:
    const unsigned int& external_data;
  public:
    /* ... */
  A (unsigned int& ext_data) : external_data(ext_data) { }
};

which won't work if one wishes to bind to a const object.  Another
alternative is

class A {
  private:
    const unsigned int& external_data;
  public:
    /* ... */
  A (const unsigned int *ext_data) : external_data(*ext_data) { }
};

which allows a null pointer to be passed.  As I said before,

> > [ ... ] Either of these misstates the interface contract (one by dropping
> > const, the other by admitting the possibility of a null pointer).

Is there a third possibility which I have missed?

Mike

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






Author: Andre@incognito.com (Andre Kostur)
Date: 2000/06/07
Raw View
I'm kinda curious as to what the rationale was behind disallowing the
binding of temporaries to non-const references?  As a (somewhat contrived)
example:

class C
{
public:
     C() {};
     C(C & c) {};
     ~C() {};
};

C fn()
{
     return C();
}

int main(int, char * [])
{
     C a(fn());
}

gcc 2.95.2 flags the construction as an error:

% g++ r.cpp -o r -Wall
r.cpp: In function `class C fn()':
r.cpp:11: initialization of non-const reference type `class C &'
r.cpp:11: from rvalue of type `C'
r.cpp:5: in passing argument 1 of `C::C(C &)'
r.cpp: In function `int main(int, char **)':
r.cpp:16: initialization of non-const reference type `class C &'
r.cpp:16: from rvalue of type `C'
r.cpp:5: in passing argument 1 of `C::C(C &)'
% g++ -v
Using builtin specs.
gcc version 2.95.2 19991024 (release)
%


What danger are we avoiding by disallowing this sort of thing?

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






Author: Stephen Cleary <scleary@jerviswebb.com>
Date: 2000/06/08
Raw View
Andre@incognito.com (Andre Kostur) wrote:
> I'm kinda curious as to what the rationale was behind disallowing the
> binding of temporaries to non-const references
>
> ...
>
> What danger are we avoiding by disallowing this sort of thing?

A function that takes a parameter by reference may modify the value of
that parameter.  If a function modifies a temporary, then its side-
effects are lost.  Temporaries may not be bound to non-const references
to prevent just that sort of programming error.

If the function doesn't have to modify the parameter, pass it by const
reference.

To handle situations where classes have "Relaxed CopyConstructible" or
"Relaxed Assignable" semantics, where the *source* of the copy is
changed, use a "ref class" to allow construction from/assignment from
function return values.  For more information, see "Fixing auto_ptr",
by Bill Gibbons and Greg Colvin, Document number J16/97-
0090R1=WG21/N1128R1.  It can be found as part of Scott Meyers' "More
Effective C++" auto_ptr update at:

http://www.awl.com/cseng/titles/0-201-63371-X/auto_ptr.html

Hope this helps!

        -Steve


Sent via Deja.com http://www.deja.com/
Before you buy.

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