Topic: non-const references to rvalues


Author: Christopher Eltschka <celtschk@web.de>
Date: Tue, 4 Jun 2002 17:21:26 GMT
Raw View
"Anthony Williams"<anthwil@nortelnetworks.com> writes:

> "John Potter" <jpotter@falcon.lhup.edu> wrote in message
> news:3cd484dd.53852109@news.earthlink.net...
> > On Fri,  3 May 2002 20:53:14 GMT, "Anthony
> > Williams"<anthwil@nortelnetworks.com> wrote:
> >
> > > Everyone knows you can't bind non-const references to rvalues.
> > > Lots of people also know that you can write a member function to perform
> a
> > > workaround
>
> > > struct X
> > > {
> > >     X& getNCRef()
> > >     {
> > >         return *this;
> > >     }
> > > };
>
> > > void f(X&);
> > > f(X().getNCRef());
>
> > > If you spell "getNCRef" "operator X&", this still works as written (i.e.
> > > with an explicit call). However, the standard also says that this
> conversion
> > > operator will never be used to convert an X to an X& (12.3.2p1). I can
> see
> > > the logic for this in general --- it would really confuse things if when
> you
> > > passed a (named) X object to a function taking an X&, the conversion
> > > operator got called (though I can imagine there might be utility in
> this).
> > > However, when it comes to rvalues, there is no "normal" conversion to
> > > conflict with. Indeed, g++ 3.0.3 will use the conversion operator for
> > > rvalues, and not for lvalues (despite warning that "conversion to a
> > > reference to the same type will never use a type conversion operator"),
> > > though Comeau C++ correctly refuses. (Test program below)
>
> > G++ 2.95.3 refuses the code.  The fictitious 2.96, and 3.0.4 accept it.
>
> > > What do people think? Should binding an rvalue to a non-const ref be
> allowed
> > > where an explicit conversion operator is provided? Such a change would
> not
> > > break any existing code, and would also limit the problems with binding
> > > non-const refs to rvalues in general, since these are mainly caused by
> > > implicit conversions. It would also give class designers the ability to
> > > decide whether or not it was appropriate --- e.g. for a TempMatrix type
> used
> > > for the intermediate result of a matrix expression, it would be a good
> idea,
> > > whereas in other circumstances it might not be.
> >
> > When the two invalid lines are commented out, 2.95.3 selects the
> > appropriate const version for the rvalues.
> >
> > The newer versions with this cute "feature" select the non-const version
> > in all cases.  Since it did not use (regardless of observable output)
> > the operator which would never be used, there was no user defined
> > conversion and the identity case was better than the qualification
> > adjustment.  An amusing feature.  It does break existing code.
>
> Is there existing code which would break if it were done "properly" (see
> below)?
>
> > The "explicit" conversion operator is used implicitly.  It is still an
> > implicit conversion.  With all of the bad things we say about implicit
> > conversions, why is this different?
>
> It is different because you have control over which classes support such an
> implicit conversion. Also, it would be classed as a "user-defined
> conversion", so you couldn't pass a char* and end up with a non-const ref to
> a string (assuming the string class in question defined both conversions),
> as that would be two user-defined conversions.
>
> > I think more thought is required befor suggesting a gcc bug as a change
> > to the standard.  The interaction with other features is surprising.
>
>
> As it is "implemented" in gcc, yes.
>
> I was suggesting that such an operator would be considered a user-defined
> conversion, and only used if the normal standard conversions didn't work ---
> i.e. if you tried to pass an rvalue to a function that only accepted
> non-const lvalues. Also, if template argument yielded a non-const ref, which
> was the "best match" overload, then the user-defined conversion could be
> invoked to implement the conversion (though this is a further extension).
>
> This should mean that only code that was currently illegal would consider
> the new user-defined conversion, and therefore shouldn't break any code.

class X
{
public:
  operator X&();
  operator int() const;
};

void f(X&);
void f(int);

int main()
{
  f(X());
}

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Tue, 4 Jun 2002 12:29:38 CST
Raw View
jpotter@falcon.lhup.edu (John Potter) writes:

> On Fri,  3 May 2002 20:53:14 GMT, "Anthony
> Williams"<anthwil@nortelnetworks.com> wrote:
>
> > Everyone knows you can't bind non-const references to rvalues.
> > Lots of people also know that you can write a member function to perform a
> > workaround
>
> > struct X
> > {
> >     X& getNCRef()
> >     {
> >         return *this;
> >     }
> > };
>
> > void f(X&);
> > f(X().getNCRef());
>
> > If you spell "getNCRef" "operator X&", this still works as written (i.e.
> > with an explicit call). However, the standard also says that this conversion
> > operator will never be used to convert an X to an X& (12.3.2p1). I can see
> > the logic for this in general --- it would really confuse things if when you
> > passed a (named) X object to a function taking an X&, the conversion
> > operator got called (though I can imagine there might be utility in this).
> > However, when it comes to rvalues, there is no "normal" conversion to
> > conflict with. Indeed, g++ 3.0.3 will use the conversion operator for
> > rvalues, and not for lvalues (despite warning that "conversion to a
> > reference to the same type will never use a type conversion operator"),
> > though Comeau C++ correctly refuses. (Test program below)
>
> G++ 2.95.3 refuses the code.  The fictitious 2.96, and 3.0.4 accept it.
>
> > What do people think? Should binding an rvalue to a non-const ref be allowed
> > where an explicit conversion operator is provided? Such a change would not
> > break any existing code, and would also limit the problems with binding
> > non-const refs to rvalues in general, since these are mainly caused by
> > implicit conversions. It would also give class designers the ability to
> > decide whether or not it was appropriate --- e.g. for a TempMatrix type used
> > for the intermediate result of a matrix expression, it would be a good idea,
> > whereas in other circumstances it might not be.
>
> When the two invalid lines are commented out, 2.95.3 selects the
> appropriate const version for the rvalues.
>
> The newer versions with this cute "feature" select the non-const version
> in all cases.  Since it did not use (regardless of observable output)
> the operator which would never be used, there was no user defined
> conversion and the identity case was better than the qualification
> adjustment.  An amusing feature.  It does break existing code.
>
> For your amusement, add some more things to the code.
>
> In X.
>     operator X& () const
>     {
>     std::cout<<"const conversion"<<std::endl;
>     return const_cast<X&>(*this); // Omit the cast for fun :)
>     }
>   Add and instrument the default and copy ctors.
>
> Global
> X const constX () {
>     std::cout << "constX()\n";
>     return X();
>     }
>
> In main.
>     fx(constX());     // const rvalue
>     gx(constX());
>     f(constX());
>     g(constX());
>
>     X const cx = X(); // named const instance
>     fx(cx);
>     gx(cx);
>     f(cx);
>     g(cx);
>
> Is this what you really want?
>
> The "explicit" conversion operator is used implicitly.  It is still an
> implicit conversion.  With all of the bad things we say about implicit
> conversions, why is this different?
>
> I think more thought is required befor suggesting a gcc bug as a change
> to the standard.  The interaction with other features is surprising.
>
> An alternative.
>
> template <class U, class T>
> U lvalue_cast (T const& rvalue) {
>     return const_cast<U>(rvalue);
>     }
>
> Use in your main.
>
>     fx(lvalue_cast<X&>(X()));
>     f(lvalue_cast<X&>(X()));
>
> It also works for fundamental types.
>
>     std::cout << ++ lvalue_cast<int&>(41) << std::endl;

But unlike the member function, it's unsafe:

  void f(Myclass&);

  Myclass const x;

  // Casting an lvalue to lvalue should be silly, but harmless.
  // However:
  f(lvalue_cast<Myclass&>(x));
  // oops, compiles. Const safety is broken.

  // However, the following doesn't compile:
  f(x.getNCref());

So your lvalue_cast is really an lvalue_and_const_cast.

The only intrinsically const-correct way to get a pointer to an rvalue
is a non-static member function (where you get it for free through the
this pointer).

>
> It must be used explicitely.
> The user is in control.

More than he wants to. He wanted to get lvalueness, but he didn't want
to remove const correctness checks (otherwise he'd used const_cast
directly).

> It requires no change to the standard.
>
> I really can't get excited about implicit hazzards to avoid explicit
> casts.

I really can't get excited about explicit casts which do more than
they announce.

However, I agree that a change to the standard is not needed for the
original proposal: Since the idea of the proposal was that the class
author is in control if the class can be rvalue-to-lvalue-converted,
well, he already *is* in control over its member functions, so he can
easily add that, and the user can easily use it when provided. Indeed,
it can do so in a rather intuitive way, overloading the adress-of
operator to return the this pointer, and have the user write *&X().

But I think the author of the _reference_ should be able to say if it
makes sense to bind it to an rvalue (actually, I can not only imagine
uses for rvalues bound to non-const references, I can also imagine
situations where I don't want rvalues be bound to const references:

  class Foo { ... };

  // MyClass objects will store a reference to Foo, but promise never
  // to change it
  class MyClass
  {
  public:
    MyClass(Foo const& foo);
    ...
  private:
    Foo const& theFoo;
  };

  class Foo2
  {
  public:
    operator Foo();
  }

  MyClass::MyClass(Foo const& foo):
    theFoo(foo)
  {
  }

  int main()
  {
    Foo2 x;
    MyClass x = MyClass(x); // oops!
    ...
  }

Unfortunately, I think there is no really clean solution to this
problem which doesn't break existing code (and quite a lot, I'd
assume). Such a clean, but code-breaking solution would be a keyword
rvalue which must be present at a reference to allow binding rvalues
to it (i.e. int& only binds to non-const lvalues, int rvalue& binds to
non-const lvalues and non-const rvalues, int const& binds only to
non-const or const lvalues, and int const rvalue& binds to everything
of type int, just as int const& does in current C++).

There are non-nice solutions like having *both* an rvalue and an
lvalue keyword, with const references defaulting to rvalue and
non-const references defaulting to lvalue:

  MyClass::MyClass(Foo const lvalue&) // may not be bound to rvalue

  ostream rvalue&
   operator<<(ostream rvalue& os,     // may be bound to rvalue
              MyClass const& mc)

Note that I made the return type ostream rvalue&, for the reason that
I would forbid binding the object bound to an rvalue reference from
there to an lvalue reference. That is, we would have an "rvalue
safety" (not completely, since member functions cannot be made rvalue
safe). Probably it would also make sense to disallow taking the
address of an rvalue reference (i.e. the object bound to it), to avoid
having to deal with rvalue pointers as well.

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Tue, 4 Jun 2002 20:36:45 GMT
Raw View
On Tue,  4 Jun 2002 17:29:28 GMT, Christopher Eltschka <celtschk@web.de>
wrote:

> jpotter@falcon.lhup.edu (John Potter) writes:

> > template <class U, class T>
> > U lvalue_cast (T const& rvalue) {
> >     return const_cast<U>(rvalue);
> >     }

> > Use in your main.

> >     fx(lvalue_cast<X&>(X()));
> >     f(lvalue_cast<X&>(X()));
> >
> > It also works for fundamental types.

> >     std::cout << ++ lvalue_cast<int&>(41) << std::endl;

> But unlike the member function, it's unsafe:

>   void f(Myclass&);

>   Myclass const x;

>   // Casting an lvalue to lvalue should be silly, but harmless.
>   // However:
>   f(lvalue_cast<Myclass&>(x));
>   // oops, compiles. Const safety is broken.

Casting is never safe, that's why we use cast in the name.

X& r = (X() = X());

Oops, compiles and produces a dangling reference.  If you want safety,
don't use casts, don't write conversion operators, don't write
conversion constructors.  And don't let those who do not know an lvalue
when they have one write C++.

John

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Anthony Williams"<anthwil@nortelnetworks.com>
Date: Fri, 3 May 2002 20:53:14 GMT
Raw View
Everyone knows you can't bind non-const references to rvalues.
Lots of people also know that you can write a member function to perform a
workaround

struct X
{
    X& getNCRef()
    {
        return *this;
    }
};

void f(X&);
f(X().getNCRef());

If you spell "getNCRef" "operator X&", this still works as written (i.e.
with an explicit call). However, the standard also says that this conversion
operator will never be used to convert an X to an X& (12.3.2p1). I can see
the logic for this in general --- it would really confuse things if when you
passed a (named) X object to a function taking an X&, the conversion
operator got called (though I can imagine there might be utility in this).
However, when it comes to rvalues, there is no "normal" conversion to
conflict with. Indeed, g++ 3.0.3 will use the conversion operator for
rvalues, and not for lvalues (despite warning that "conversion to a
reference to the same type will never use a type conversion operator"),
though Comeau C++ correctly refuses. (Test program below)

What do people think? Should binding an rvalue to a non-const ref be allowed
where an explicit conversion operator is provided? Such a change would not
break any existing code, and would also limit the problems with binding
non-const refs to rvalues in general, since these are mainly caused by
implicit conversions. It would also give class designers the ability to
decide whether or not it was appropriate --- e.g. for a TempMatrix type used
for the intermediate result of a matrix expression, it would be a good idea,
whereas in other circumstances it might not be.

Anthony
********************************
// Test program for conversion operators and non-const references to rvalues

#include <iostream>

struct X
{
    operator X&()
    {

 std::cout<<"conversion"<<std::endl;
 return *this;
    }
};

void fx(X&)
{}

void gx(X&)
{
    std::cout<<"Non-const"<<std::endl;
}

void gx(const X&)
{
    std::cout<<"Const"<<std::endl;
}

template<typename T>
void f(T&)
{}

template<typename T>
void g(T&)
{
    std::cout<<"Non-const template"<<std::endl;
}

template<typename T>
void g(const T&)
{
    std::cout<<"const template"<<std::endl;
}


int main()
{
    fx(X());
    gx(X());
    f(X());
    g(X());

    X xx; // named instance
    fx(xx);
    gx(xx);
    f(xx);
    g(xx);
}
********************************
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer


---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: Mon, 6 May 2002 07:53:22 GMT
Raw View
On Fri,  3 May 2002 20:53:14 GMT, "Anthony
Williams"<anthwil@nortelnetworks.com> wrote:

> Everyone knows you can't bind non-const references to rvalues.
> Lots of people also know that you can write a member function to perform a
> workaround

> struct X
> {
>     X& getNCRef()
>     {
>         return *this;
>     }
> };

> void f(X&);
> f(X().getNCRef());

> If you spell "getNCRef" "operator X&", this still works as written (i.e.
> with an explicit call). However, the standard also says that this conversion
> operator will never be used to convert an X to an X& (12.3.2p1). I can see
> the logic for this in general --- it would really confuse things if when you
> passed a (named) X object to a function taking an X&, the conversion
> operator got called (though I can imagine there might be utility in this).
> However, when it comes to rvalues, there is no "normal" conversion to
> conflict with. Indeed, g++ 3.0.3 will use the conversion operator for
> rvalues, and not for lvalues (despite warning that "conversion to a
> reference to the same type will never use a type conversion operator"),
> though Comeau C++ correctly refuses. (Test program below)

G++ 2.95.3 refuses the code.  The fictitious 2.96, and 3.0.4 accept it.

> What do people think? Should binding an rvalue to a non-const ref be allowed
> where an explicit conversion operator is provided? Such a change would not
> break any existing code, and would also limit the problems with binding
> non-const refs to rvalues in general, since these are mainly caused by
> implicit conversions. It would also give class designers the ability to
> decide whether or not it was appropriate --- e.g. for a TempMatrix type used
> for the intermediate result of a matrix expression, it would be a good idea,
> whereas in other circumstances it might not be.

When the two invalid lines are commented out, 2.95.3 selects the
appropriate const version for the rvalues.

The newer versions with this cute "feature" select the non-const version
in all cases.  Since it did not use (regardless of observable output)
the operator which would never be used, there was no user defined
conversion and the identity case was better than the qualification
adjustment.  An amusing feature.  It does break existing code.

For your amusement, add some more things to the code.

In X.
    operator X& () const
    {
    std::cout<<"const conversion"<<std::endl;
    return const_cast<X&>(*this); // Omit the cast for fun :)
    }
  Add and instrument the default and copy ctors.

Global
X const constX () {
    std::cout << "constX()\n";
    return X();
    }

In main.
    fx(constX());     // const rvalue
    gx(constX());
    f(constX());
    g(constX());

    X const cx = X(); // named const instance
    fx(cx);
    gx(cx);
    f(cx);
    g(cx);

Is this what you really want?

The "explicit" conversion operator is used implicitly.  It is still an
implicit conversion.  With all of the bad things we say about implicit
conversions, why is this different?

I think more thought is required befor suggesting a gcc bug as a change
to the standard.  The interaction with other features is surprising.

An alternative.

template <class U, class T>
U lvalue_cast (T const& rvalue) {
    return const_cast<U>(rvalue);
    }

Use in your main.

    fx(lvalue_cast<X&>(X()));
    f(lvalue_cast<X&>(X()));

It also works for fundamental types.

    std::cout << ++ lvalue_cast<int&>(41) << std::endl;

It must be used explicitely.
The user is in control.
It requires no change to the standard.

I really can't get excited about implicit hazzards to avoid explicit
casts.

John

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Anthony Williams"<anthwil@nortelnetworks.com>
Date: Tue, 7 May 2002 15:54:16 GMT
Raw View
"John Potter" <jpotter@falcon.lhup.edu> wrote in message
news:3cd484dd.53852109@news.earthlink.net...
> On Fri,  3 May 2002 20:53:14 GMT, "Anthony
> Williams"<anthwil@nortelnetworks.com> wrote:
>
> > Everyone knows you can't bind non-const references to rvalues.
> > Lots of people also know that you can write a member function to perform
a
> > workaround

> > struct X
> > {
> >     X& getNCRef()
> >     {
> >         return *this;
> >     }
> > };

> > void f(X&);
> > f(X().getNCRef());

> > If you spell "getNCRef" "operator X&", this still works as written (i.e.
> > with an explicit call). However, the standard also says that this
conversion
> > operator will never be used to convert an X to an X& (12.3.2p1). I can
see
> > the logic for this in general --- it would really confuse things if when
you
> > passed a (named) X object to a function taking an X&, the conversion
> > operator got called (though I can imagine there might be utility in
this).
> > However, when it comes to rvalues, there is no "normal" conversion to
> > conflict with. Indeed, g++ 3.0.3 will use the conversion operator for
> > rvalues, and not for lvalues (despite warning that "conversion to a
> > reference to the same type will never use a type conversion operator"),
> > though Comeau C++ correctly refuses. (Test program below)

> G++ 2.95.3 refuses the code.  The fictitious 2.96, and 3.0.4 accept it.

> > What do people think? Should binding an rvalue to a non-const ref be
allowed
> > where an explicit conversion operator is provided? Such a change would
not
> > break any existing code, and would also limit the problems with binding
> > non-const refs to rvalues in general, since these are mainly caused by
> > implicit conversions. It would also give class designers the ability to
> > decide whether or not it was appropriate --- e.g. for a TempMatrix type
used
> > for the intermediate result of a matrix expression, it would be a good
idea,
> > whereas in other circumstances it might not be.
>
> When the two invalid lines are commented out, 2.95.3 selects the
> appropriate const version for the rvalues.
>
> The newer versions with this cute "feature" select the non-const version
> in all cases.  Since it did not use (regardless of observable output)
> the operator which would never be used, there was no user defined
> conversion and the identity case was better than the qualification
> adjustment.  An amusing feature.  It does break existing code.

Is there existing code which would break if it were done "properly" (see
below)?

> The "explicit" conversion operator is used implicitly.  It is still an
> implicit conversion.  With all of the bad things we say about implicit
> conversions, why is this different?

It is different because you have control over which classes support such an
implicit conversion. Also, it would be classed as a "user-defined
conversion", so you couldn't pass a char* and end up with a non-const ref to
a string (assuming the string class in question defined both conversions),
as that would be two user-defined conversions.

> I think more thought is required befor suggesting a gcc bug as a change
> to the standard.  The interaction with other features is surprising.


As it is "implemented" in gcc, yes.

I was suggesting that such an operator would be considered a user-defined
conversion, and only used if the normal standard conversions didn't work ---
i.e. if you tried to pass an rvalue to a function that only accepted
non-const lvalues. Also, if template argument yielded a non-const ref, which
was the "best match" overload, then the user-defined conversion could be
invoked to implement the conversion (though this is a further extension).

This should mean that only code that was currently illegal would consider
the new user-defined conversion, and therefore shouldn't break any code.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer


---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]