Topic: The real problem with allowing build-in rvalue reference to be modified, Was: (N2118)Rvalue reference may create a loophole in the type system


Author: pongba@gmail.com
Date: Mon, 23 Jul 2007 01:41:22 CST
Raw View
On Jul 23, 3:16 am, David Abrahams <d...@boost-consulting.com> wrote:
> on Sat Jul 21 2007, Mathias Gaunard <loufoque-AT-gmail.com> wrote:
>
> > On Jul 20, 5:47 pm, d...@boost-consulting.com (David Abrahams) wrote:
>
> >> > double d = 0;
> >> > int&& r = d;
> >> > r++; // a *temporary* is incremented, which has nothing to do with d
>
> >> Your comment is surprising.  What makes you think a temporary is
> >> incremented, and d isn't incremented, here?
>
> > Because double isn't int.
>
> Ohhhh...  I missed that part, which I guess is the whole point.
>
> > The double is converted to an int, hence introducing a temporary.
>
> Right.

Suppose you wrote this code some day:

int num = ...;
. // several lines of code that's long enough to push the above line
outside your sight
int&& r = num;
r++; // num being incremented

and some days later you "accidentally" changed the type of num into
double, as the following:

double num = ...;
. // several lines of code that's long enough to push the above line
outside your sight
int&& r = num;
r++; // oops!

As we know, the code compiles without any problem, but the behavior
has been silently changed(i.e. now r++ wouldn't increment num anymore
and you wouldn't get a hint from the compiler that says "hey, buddy,
aren't you forgetting something?", which used to be the case with
"traditional"(lvalue) reference).

And of course the code is so contrived, but we can hopefully make up
some other code that's not so contrived, for example, what if the
rvalue reference is a parameter of a function:

void f(int&& r);

int num; // someday you change the type of num into double, and the
behavior of the code will be silently changed.

f(num);

Or what if the user just accidentally mistyped "&" into "&&", i.e.

he should've typed:

int& r = num;
r++;

but instead he typed:

int&& r = num;
r++;

silent failure!

but hey, everybody is allowed to make typing mistakes, and it's the
grand type system that'll guard the door, right?

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Mon, 23 Jul 2007 16:01:49 GMT
Raw View
In article <1185159483.702063.252270@d30g2000prg.googlegroups.com>,
 pongba@gmail.com wrote:

> Suppose you wrote this code some day:
>
> int num = ...;
> . // several lines of code that's long enough to push the above line
> outside your sight
> int&& r = num;
> r++; // num being incremented
>
> and some days later you "accidentally" changed the type of num into
> double, as the following:
>
> double num = ...;
> . // several lines of code that's long enough to push the above line
> outside your sight
> int&& r = num;
> r++; // oops!
>
> As we know, the code compiles without any problem, but the behavior
> has been silently changed(i.e. now r++ wouldn't increment num anymore
> and you wouldn't get a hint from the compiler that says "hey, buddy,
> aren't you forgetting something?", which used to be the case with
> "traditional"(lvalue) reference).
>
> And of course the code is so contrived, but we can hopefully make up
> some other code that's not so contrived, for example, what if the
> rvalue reference is a parameter of a function:
>
> void f(int&& r);
>
> int num; // someday you change the type of num into double, and the
> behavior of the code will be silently changed.
>
> f(num);
>
> Or what if the user just accidentally mistyped "&" into "&&", i.e.
>
> he should've typed:
>
> int& r = num;
> r++;
>
> but instead he typed:
>
> int&& r = num;
> r++;
>
> silent failure!
>
> but hey, everybody is allowed to make typing mistakes, and it's the
> grand type system that'll guard the door, right?

So what do you suggest?

> IMO the wording(N2118) should be strengthened, so that for build
> operators, rvalue references are treated as rvalue, thus preventing i+
> + from compiling.

I disagree.  In the past, whenever the language has treated "built-in"
types differently than user-defined types, the net result has been to
complicate the language for the end user.  Your recommendation is not
without cost, and I do not believe the benefits of such a change
outweigh the disadvantages.

Example:

class my_pointer
{
    int* p_;
public:
    my_pointer(int*&& p) : p_(p) {p = 0;}
    ~my_pointer() {delete p_;}
    my_pointer(const my_pointer&) = delete;
    my_pointer& operator=(const my_pointer&) = delete;
};

#include <cassert>

int main()
{
    int* p = new int;
    my_pointer m1(p);
    assert(p == 0);
    my_pointer m2(new int);
}

The my_pointer ctor above treats the variable p of built-in type int*&&
as an lvalue inside of the ctor.  The above seems like reasonable code
to me.  The author wants to accept both rvalue and lvalue pointers, and
modify the pointer regardless of whether or not it is an rvalue.

Rvalue-ref introduces a choice for the programmer:  bind an rvalue to a
reference or not.  It doesn't strike me as unusual that the introduction
of the choice at the same time introduces the danger of making the wrong
choice.  That is the nature of choices.

The real question, imho, is:  Does the introduction of this choice offer
benefits that outweigh the disadvantages of introducing the choice?

I maintain that the hands down answer to that question is yes.  Perfect
forwarding and move semantics (which work synergistically together) is
very strong motivation.  Furthermore, in top-level code it is not
unusual to get these benefits without the client ever seeing an explicit
rvalue reference (the syntax remains at lower levels).

Sure, if you write f(int&& r), and meant f(int& r), bad things can
happen.  Similarly if you write

   if (x = y)

instead of

    if (x == y)

bad things can happen.

If you write +r, instead of ++r, bad things can happen.

If you write if (x & y) instead of if (x && y), bad things can happen.

I suspect I could go on like this all day...  Language design is full of
engineering tradeoffs, and even more so when you're talking about adding
to an existing language.

-Howard

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: Peter Dimov <pdimov@gmail.com>
Date: Mon, 23 Jul 2007 23:01:13 CST
Raw View
On Jul 23, 10:41 am, pon...@gmail.com wrote:

> Suppose you wrote this code some day:
>
> int num = ...;
> . // several lines of code that's long enough to push the above line
> outside your sight
> int&& r = num;

This line makes no sense in any context. You simply should not have
written it. Any cursory code review will catch it.

The more interesting example is

void f( int&& r );

f( num );

where you later change the type of num to double. Yes, it's error
prone. It is your choice as a designer of f to make its interface
error-prone or not. We could have banned implicit conversions with a
rvalue ref target (I remember raising the issue once or twice), but we
didn't, and I don't consider it a big problem. Rvalue refs are sharp
by their nature and there are many ways to cut oneself.

---
[ 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.comeaucomputing.com/csc/faq.html                      ]





Author: pongba <pongba@gmail.com>
Date: Tue, 24 Jul 2007 00:59:34 CST
Raw View
On Jul 24, 12:01 am, howard.hinn...@gmail.com (Howard Hinnant) wrote:
> In article <1185159483.702063.252...@d30g2000prg.googlegroups.com>,
>
>
>
>  pon...@gmail.com wrote:
> > Suppose you wrote this code some day:
>
> > int num = ...;
> > . // several lines of code that's long enough to push the above line
> > outside your sight
> > int&& r = num;
> > r++; // num being incremented
>
> > and some days later you "accidentally" changed the type of num into
> > double, as the following:
>
> > double num = ...;
> > . // several lines of code that's long enough to push the above line
> > outside your sight
> > int&& r = num;
> > r++; // oops!
>
> > As we know, the code compiles without any problem, but the behavior
> > has been silently changed(i.e. now r++ wouldn't increment num anymore
> > and you wouldn't get a hint from the compiler that says "hey, buddy,
> > aren't you forgetting something?", which used to be the case with
> > "traditional"(lvalue) reference).
>
> > And of course the code is so contrived, but we can hopefully make up
> > some other code that's not so contrived, for example, what if the
> > rvalue reference is a parameter of a function:
>
> > void f(int&& r);
>
> > int num; // someday you change the type of num into double, and the
> > behavior of the code will be silently changed.
>
> > f(num);
>
> > Or what if the user just accidentally mistyped "&" into "&&", i.e.
>
> > he should've typed:
>
> > int& r = num;
> > r++;
>
> > but instead he typed:
>
> > int&& r = num;
> > r++;
>
> > silent failure!
>
> > but hey, everybody is allowed to make typing mistakes, and it's the
> > grand type system that'll guard the door, right?
>
> So what do you suggest?
>
> > IMO the wording(N2118) should be strengthened, so that for build
> > operators, rvalue references are treated as rvalue, thus preventing i+
> > + from compiling.
>
> I disagree.  In the past, whenever the language has treated "built-in"
> types differently than user-defined types, the net result has been to
> complicate the language for the end user.  Your recommendation is not
> without cost, and I do not believe the benefits of such a change
> outweigh the disadvantages.
>
> Example:
>
> class my_pointer
> {
>     int* p_;
> public:
>     my_pointer(int*&& p) : p_(p) {p = 0;}
>     ~my_pointer() {delete p_;}
>     my_pointer(const my_pointer&) = delete;
>     my_pointer& operator=(const my_pointer&) = delete;
>
> };
>
> #include <cassert>
>
> int main()
> {
>     int* p = new int;
>     my_pointer m1(p);
>     assert(p == 0);
>     my_pointer m2(new int);
>
> }
>
> The my_pointer ctor above treats the variable p of built-in type int*&&
> as an lvalue inside of the ctor.  The above seems like reasonable code
> to me.  The author wants to accept both rvalue and lvalue pointers, and
> modify the pointer regardless of whether or not it is an rvalue.

This is a very good point. I'm convinced. Thanks, Howard:)
It seems using rvalue reference as a catch-alll reference has its own
usage, right?

>
> Rvalue-ref introduces a choice for the programmer:  bind an rvalue to a
> reference or not.  It doesn't strike me as unusual that the introduction
> of the choice at the same time introduces the danger of making the wrong
> choice.  That is the nature of choices.
>
> The real question, imho, is:  Does the introduction of this choice offer
> benefits that outweigh the disadvantages of introducing the choice?
>
> I maintain that the hands down answer to that question is yes.  Perfect
> forwarding and move semantics (which work synergistically together) is
> very strong motivation.  Furthermore, in top-level code it is not
> unusual to get these benefits without the client ever seeing an explicit
> rvalue reference (the syntax remains at lower levels).
>
> Sure, if you write f(int&& r), and meant f(int& r), bad things can
> happen.  Similarly if you write
>
>    if (x = y)
>
> instead of
>
>     if (x == y)
>
> bad things can happen.
>
> If you write +r, instead of ++r, bad things can happen.
>
> If you write if (x & y) instead of if (x && y), bad things can happen.
>
> I suspect I could go on like this all day...  Language design is full of
> engineering tradeoffs, and even more so when you're talking about adding
> to an existing language.

Excellent argument, I'm convinced even more :)

---
[ 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.comeaucomputing.com/csc/faq.html                      ]