Topic: So a reference to temporary must be const? why?
Author: clarkson@csl.sony.co.jp (Brian Clarkson)
Date: Tue, 25 Jan 2005 03:05:01 GMT Raw View
Hello.
I came across this problem while porting some code from vc++ to gcc.
From my shaky understanding of the ANSI C++ standard, temporaries are
guaranteed to stick around at least until right before execution of the
next line (or up until the semicolon). However, it seems that a
non-const reference to a temporary is forbidden. Isn' t this way too
strict?
I have distilled the issue to the following example:
****begin test.cpp****
#include <iostream>
using namespace std;
class vector {
public:
vector(int x_, int y_) : x(x_), y(y_) {}
vector(const vector& xx) : x(xx.x), y(xx.y) {}
vector& operator=(const vector& xx) { x = xx.x; y = xx.y; return
*this; }
friend vector operator+(const vector& a, const vector& b);
public:
int x, y;
};
vector operator+(const vector& a, const vector& b) {
// vector tmp(a.x + b.x, a.y + b.y);
// return tmp;
return vector(a.x + b.x, a.y + b.y);
}
void f(vector& v)
{
cout << "vector = (" << v.x << ", " << v.y << ")" << endl;
}
int main()
{
vector a(1, 2);
vector b(2, 3);
f(a+b); // this line generates an error
return 0;
}
****end test.cpp****
Which when compiled gives the following output:
g++ test.cpp
test.cpp: In function `int main()':
test.cpp:32: error: could not convert `operator+(const vector&, const
vector&)((&b))' to `vector&'
test.cpp:23: error: in passing argument 1 of `void f(vector&)'
If f() is defined as f(const vector&) then this example compiles, but
it seems to me that operating on a reference to the temporary that is
generated from (a+b) should be safe since we are finished with it
before the semicolon.
Is there a better way to do this? (i.e. no explicit temporaries and no
unnecessary copies)
Thanks in advance.
Brian
---
[ 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: "jacobsson@gmail.com" <jacobsson@gmail.com>
Date: Tue, 25 Jan 2005 09:23:56 CST Raw View
Temporary objects are lvalues in VC7, i.e., they can be passed as
non-const references. In GCC, temporary objects are rvalues and can
only be passed as const references:
class X;
void foo(X& x)
{
x.y = 2;
}
void bar()
{
// BAD: temporary X object is not an lvalue
foo(X(10));
}
daniel
Brian Clarkson wrote:
> Hello.
>
> I came across this problem while porting some code from vc++ to gcc.
> From my shaky understanding of the ANSI C++ standard, temporaries
are
> guaranteed to stick around at least until right before execution of
the
> next line (or up until the semicolon). However, it seems that a
> non-const reference to a temporary is forbidden. Isn' t this way too
> strict?
>
> I have distilled the issue to the following example:
>
> ****begin test.cpp****
>
> #include <iostream>
> using namespace std;
>
> class vector {
> public:
> vector(int x_, int y_) : x(x_), y(y_) {}
> vector(const vector& xx) : x(xx.x), y(xx.y) {}
> vector& operator=(const vector& xx) { x = xx.x; y = xx.y; return
> *this; }
>
> friend vector operator+(const vector& a, const vector& b);
> public:
> int x, y;
> };
>
> vector operator+(const vector& a, const vector& b) {
> // vector tmp(a.x + b.x, a.y + b.y);
> // return tmp;
> return vector(a.x + b.x, a.y + b.y);
> }
>
> void f(vector& v)
> {
> cout << "vector = (" << v.x << ", " << v.y << ")" << endl;
> }
>
> int main()
> {
> vector a(1, 2);
> vector b(2, 3);
>
> f(a+b); // this line generates an error
>
> return 0;
> }
>
> ****end test.cpp****
>
> Which when compiled gives the following output:
>
> g++ test.cpp
>
> test.cpp: In function `int main()':
> test.cpp:32: error: could not convert `operator+(const vector&, const
> vector&)((&b))' to `vector&'
> test.cpp:23: error: in passing argument 1 of `void f(vector&)'
>
>
> If f() is defined as f(const vector&) then this example compiles, but
> it seems to me that operating on a reference to the temporary that is
> generated from (a+b) should be safe since we are finished with it
> before the semicolon.
>
> Is there a better way to do this? (i.e. no explicit temporaries and
no
> unnecessary copies)
>
> Thanks in advance.
>
> Brian
>
> ---
> [ 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
]
---
[ 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: "msalters" <Michiel.Salters@logicacmg.com>
Date: Tue, 25 Jan 2005 12:00:25 CST Raw View
Brian Clarkson wrote:
> I came across this problem while porting some code from vc++ to gcc.
> From my shaky understanding of the ANSI C++ standard, temporaries
are
> guaranteed to stick around at least until right before execution of
the
> next line (or up until the semicolon). However, it seems that a
> non-const reference to a temporary is forbidden. Isn' t this way too
> strict?
"next line" doesn't really exist in C++, only the preprocessor
cares about lines. "The next semicolon" is a nice approximation,
except of course that quoted or commented doesn't count etc.
Binding references to non-temporaries was tried back in the eighties,
when C++ was still very much a research project. It turned out to
be a source of bugs. Basically, references were used to modify
these temporaries, and the resulting modifications were then lost
once the temporary was destroyed.
> I have distilled the issue to the following example:
> #include <iostream>
> using namespace std;
>
> class vector {
> public:
> vector(int x_, int y_) : x(x_), y(y_) {}
> public:
> int x, y;
> };
>
> vector operator+(const vector& a, const vector& b) {
> return vector(a.x + b.x, a.y + b.y);
> }
>
> void f(vector& v)
> {
> cout << "vector = (" << v.x << ", " << v.y << ")" << endl;
> }
Obviously, f doesn't need to change v, so you just missed a const here.
> int main()
> {
> vector a(1, 2);
> vector b(2, 3);
>
> f(a+b); // this line generates an error
>
> return 0;
> }
> If f() is defined as f(const vector&) then this example compiles, but
> it seems to me that operating on a reference to the temporary that is
> generated from (a+b) should be safe since we are finished with it
> before the semicolon.
No, it's not safe, because any modification by f will be lost. There's
no place to store it. But if f doesn't modify (a+b), it can take its
argument by const reference. You must choose. Mutating functions just
don't work with temporaries.
Regards,
Michiel Salters
---
[ 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: ianmcc@physik.rwth-aachen.de (Ian McCulloch)
Date: Tue, 25 Jan 2005 18:54:06 GMT Raw View
msalters wrote:
> No, it's not safe, because any modification by f will be lost. There's
> no place to store it. But if f doesn't modify (a+b), it can take its
> argument by const reference. You must choose. Mutating functions just
> don't work with temporaries.
Except when the temporary is a proxy. ;)
But maybe rvalue references
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html ) will
fix that case?
Cheers,
Ian McCulloch
---
[ 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: hinnant@metrowerks.com (Howard Hinnant)
Date: Tue, 25 Jan 2005 22:36:07 GMT Raw View
In article <35niefF312m05U1@news.dfncis.de>,
ianmcc@physik.rwth-aachen.de (Ian McCulloch) wrote:
> msalters wrote:
>
> > No, it's not safe, because any modification by f will be lost. There's
> > no place to store it. But if f doesn't modify (a+b), it can take its
> > argument by const reference. You must choose. Mutating functions just
> > don't work with temporaries.
>
> Except when the temporary is a proxy. ;)
>
> But maybe rvalue references
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html ) will
> fix that case?
Half way. The rvalue reference allows the function author to say: let
temporaries bind to this non-const reference (perhaps to allow
proxies?). However if you're the proxy author, not the function author,
then you have no say in the matter.
What is needed is a way for the proxy author to say: this temporary is
special, let it always bind to a non-const (lvalue) reference. Possible
syntax for that might be:
struct my_proxy
{
operator my_proxy&() {return *this;}
};
-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.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Wed, 26 Jan 2005 05:06:45 GMT Raw View
Ian McCulloch wrote:
> msalters wrote:
>
>
>>No, it's not safe, because any modification by f will be lost. There's
>>no place to store it. But if f doesn't modify (a+b), it can take its
>>argument by const reference. You must choose. Mutating functions just
>>don't work with temporaries.
Arguably this goes against a general design principle of C++,
"You're allowed to do it even it it's stupid".
One could argue that it should be allowed to write
(a + b) = c + d;
on the grounds that it is possible to define a, b,
and the relevant "operator+" in such a way that this
does something marginally useful.
For example, consider
bool errout; int a,b,c;
(errout ? cerr : cout) << a << b << c;
The ability to use the "?" operator in an LHS would be
useful.
I'm not seriously proposing this,. But this is where the
non-const reference proposals ("&&", and all that) lead.
John Nagle
Animats
---
[ 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: kanze@gabi-soft.fr
Date: Tue, 25 Jan 2005 23:05:48 CST Raw View
Brian Clarkson wrote:
> I came across this problem while porting some code from vc++
> to gcc. From my shaky understanding of the ANSI C++ standard,
> temporaries are guaranteed to stick around at least until
> right before execution of the next line (or up until the
> semicolon). However, it seems that a non-const reference to a
> temporary is forbidden. Isn' t this way too strict?
Maybe.
The rule wasn't in the earliest versions of C++ that I knew. It
was introduced because allowing binding a temporary to a
non-const turned out to be excessively error-prone; e.g:
| void
| incr( int& i )
| {
| ++ i ;
| }
| void
| f()
| {
| unsigned i = 0 ;
| incr( i ) ;
| // i sitll equal 0 !
| }
The real problem, of course, is that temporaries are often
invisible. Presumably, in f() above, the user wanted to modify
i when he called incr. Because of the implicit type conversion,
however, he only modified a temporary, and the results of the
modification disappeared at the end of the full expression.
The rule was introduced to solve a real problem. Whatever else,
expressions like "incr( i )" above must be illegal. There is
some argument, however, that binding an explicit temporary to an
lvalue should be allowed; that only the results of an implicit
type conversion should be banned. I don't think that there is a
proposal in this sense before the committee, however.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France, +33 (0)1 30 23 00 34
---
[ 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: "akrylik" <clarkson@csl.sony.co.jp>
Date: Wed, 26 Jan 2005 00:23:38 CST Raw View
Proxy! That's exactly what I want to use the temporary for, I just
didn't know that is what it's called. (FYI, I want to be able to write
expressions using temporary "views" of vectors, like in matlab: x(2:4)
= y(1:5)+5 and so on.)
So here is something weird (to me at least). I tried Howard's
suggestion and added a type conversion operator to the vector example
like this:
class vector {
public:
vector(int x_, int y_) : x(x_), y(y_) {}
vector(const vector& xx) : x(xx.x), y(xx.y) {}
vector& operator=(const vector& xx) { x = xx.x; y = xx.y; return
*this; }
operator vector&(){return *this;}
int x, y;
};
And compiling gives the following result:
>> g++ test.cpp
test.cpp:11: warning: conversion to a reference to the same type will
never use
a type conversion operator
But its just a warning and the code runs. Is this just badly worded
warning?
(BTW, thanks everyone for your responses.)
-Brian
---
[ 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: ianmcc@physik.rwth-aachen.de (Ian McCulloch)
Date: Wed, 26 Jan 2005 07:09:58 GMT Raw View
John Nagle wrote:
> Ian McCulloch wrote:
>> msalters wrote:
>>
>>
>>>No, it's not safe, because any modification by f will be lost. There's
>>>no place to store it. But if f doesn't modify (a+b), it can take its
>>>argument by const reference. You must choose. Mutating functions just
>>>don't work with temporaries.
>
> Arguably this goes against a general design principle of C++,
> "You're allowed to do it even it it's stupid".
>
> One could argue that it should be allowed to write
>
> (a + b) = c + d;
>
> on the grounds that it is possible to define a, b,
> and the relevant "operator+" in such a way that this
> does something marginally useful.
? You don't need to argue for it, this is already possible. You can't bind
a temporary to a non-const reference, but you *can* call a non-const
operator with one (such as the assignment operator):
// nothing out of the ordinary here
struct foo
{
foo(double x) : x_(x) {}
foo& operator=(foo const& n) { x_ = n.x_; return *this; }
double x_;
};
// nor here
foo operator+(foo const& a, foo const& b)
{
return foo(a.x_ + b.x_);
}
typedef double foo;
int main()
{
foo a(1), b(2), c(3), d(4);
(a + b) = c + d; // hmm
}
>
> For example, consider
>
> bool errout; int a,b,c;
> (errout ? cerr : cout) << a << b << c;
>
> The ability to use the "?" operator in an LHS would be
> useful.
This used to be possible, sometimes, when iostream classes had operator<< as
a member function.
> I'm not seriously proposing this,. But this is where the
> non-const reference proposals ("&&", and all that) lead.
I'm not sure that && makes it any worse, and there are certainly places
where && makes a big improvement in const-safety. For proxies that follow
the logical constness of their pointee (so a const proxy is rougly the same
as a proxy to a const), the copy ctor ought to take its argument by
non-const reference, otherwise it is possible to constuct a non-const proxy
out of a const proxy. But this is ordinarily not possible. But a copy
ctor of foo::foo(foo&&) would do the trick, AFAICT.
Cheers,
Ian McCulloch
---
[ 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: mlanzkron@yahoo.co.uk (Motti Lanzkron)
Date: Wed, 26 Jan 2005 07:11:34 GMT Raw View
Howard Hinnant wrote:
>In article <35niefF312m05U1@news.dfncis.de>,
> ianmcc@physik.rwth-aachen.de (Ian McCulloch) wrote:
>
>> msalters wrote:
>>
>> > No, it's not safe, because any modification by f will be lost. There's
>> > no place to store it. But if f doesn't modify (a+b), it can take its
>> > argument by const reference. You must choose. Mutating functions just
>> > don't work with temporaries.
>>
>> Except when the temporary is a proxy. ;)
>>
>> But maybe rvalue references
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html ) will
>> fix that case?
>
>Half way. The rvalue reference allows the function author to say: let
>temporaries bind to this non-const reference (perhaps to allow
>proxies?). However if you're the proxy author, not the function author,
>then you have no say in the matter.
>
>What is needed is a way for the proxy author to say: this temporary is
>special, let it always bind to a non-const (lvalue) reference. Possible
>syntax for that might be:
>
>struct my_proxy
>{
> operator my_proxy&() {return *this;}
>};
That's a very neat idea. Unfortunately Comeau says:
"ComeauTest.c", line 3: warning: "my_proxy::operator my_proxy &()"
will not be
called for implicit or explicit conversions
operator my_proxy&() {return *this;}
^
Section 12.3.2/1 [thanks Ben Hutchings for an old post regarding
operator void()] says:
.. A conversion function is never used to convert a (possibly
cv-qualified) object to the (possibly cv-qualified) same object type
(or a reference to it), to a (possibly cv-qualified) base class of
that type (or a reference to it), or to (possibly cv-qualified)
void.[103]
[103] Even though never directly called to perform a conversion such
conversion functions can be declared and can potentially be reached
through a call to a virtual conversion function in a base class.
I'm not sure why this is disallowed (especially in the void case).
I took the foot-note to mean that this would be OK;
struct my_proxy;
struct my_proxy_base {
virtual operator my_proxy&() = 0;
};
struct my_proxy : my_proxy_base
{
virtual operator my_proxy&() {return *this;}
};
void test(my_proxy&);
test(my_proxy());
But (at least according to Comeau) the following conversion chain
isn't considered:
rvalue my_proxy -> rvalue my_proxy_base
rvalue my_proxy_base -> operator my_proxy&
---
[ 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: ben-public-nospam@decadentplace.org.uk (Ben Hutchings)
Date: Wed, 26 Jan 2005 16:00:20 GMT Raw View
John Nagle wrote:
<snip>
> For example, consider
>
> bool errout; int a,b,c;
> (errout ? cerr : cout) << a << b << c;
>
> The ability to use the "?" operator in an LHS would be
> useful.
<snip>
It already exists; if the second and third operands of the conditional
operator are lvalues of the same type, as cerr and cout are, then so
is its result.
---
[ 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: ianmcc@physik.rwth-aachen.de (Ian McCulloch)
Date: Wed, 26 Jan 2005 16:00:36 GMT Raw View
Howard Hinnant wrote:
> In article <35niefF312m05U1@news.dfncis.de>,
> ianmcc@physik.rwth-aachen.de (Ian McCulloch) wrote:
>
>> msalters wrote:
>>
>> > No, it's not safe, because any modification by f will be lost. There's
>> > no place to store it. But if f doesn't modify (a+b), it can take its
>> > argument by const reference. You must choose. Mutating functions just
>> > don't work with temporaries.
>>
>> Except when the temporary is a proxy. ;)
>>
>> But maybe rvalue references
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html )
>> will fix that case?
>
> Half way. The rvalue reference allows the function author to say: let
> temporaries bind to this non-const reference (perhaps to allow
> proxies?). However if you're the proxy author, not the function author,
> then you have no say in the matter.
>
> What is needed is a way for the proxy author to say: this temporary is
> special, let it always bind to a non-const (lvalue) reference. Possible
> syntax for that might be:
>
> struct my_proxy
> {
> operator my_proxy&() {return *this;}
> };
Interesting. I had actually given up on the idea of totally transparent
proxying, because if the function author does want to do something with the
proxy beyond the lifetime of the temporary proxy, then AFAICT it needs
special behaviour anyway. eg.
template <typename ValueOrProxy>
struct UniversalProxy
{
UniveralProxy(ValueOrProxy& x) : x_(x) {}
operator ValueOrProxy&() { return x_; }
operator ValueOrProxy const&() { return x_; }
// what type does x_ have? Assume is_proxy is available
typedef typename boost::mpl::if_<
is_proxy<ValueOrProxy>,
ValueOrProxy, ValueOrProxy&
>::type ref_type;
ref_type x_;
};
// this really ought to work for rvalue-references,
// but only if the rvalue is a proxy.
template <typename T>
UniversalProxy<T> make_proxy(T& x)
{
return UniversalProxy<T>(x);
}
// nothing can be done to make this safer?
template <typename T>
UniversalProxy<T const> make_proxy(T const& x)
{
return UniversalProxy<T const>(x);
}
With the operator& solution, the non-const version of make_proxy would work
properly, so that is a big improvement. But is_proxy is needed in the
implementation of UniversalProxy anyway, I think. With rvalue references,
you need enable_if on is_proxy to avoid taking an rvalue reference of a
value type, which adds a lot of syntax.
Anyway, either solution is better than the current version, which can
violate const-correctness:
template <typename T>
typename boost::enable_if<is_proxy<T>, UniversalProxy<T> >::type
make_proxy(T const& x)
{
return UniversalProxy<T>(const_cast<T&>(x));
}
Is there a better way of specifying the ref_type in UniversalProxy?
Cheers,
Ian McCulloch
---
[ 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: "msalters" <Michiel.Salters@logicacmg.com>
Date: Fri, 28 Jan 2005 11:59:01 CST Raw View
Ben Hutchings wrote:
> John Nagle wrote:
> <snip>
> > For example, consider
> >
> > bool errout; int a,b,c;
> > (errout ? cerr : cout) << a << b << c;
> >
> > The ability to use the "?" operator in an LHS would be
> > useful.
> <snip>
>
> It already exists; if the second and third operands of the
conditional
> operator are lvalues of the same type, as cerr and cout are, then so
> is its result.
and for non-builtins you can always do
/**/ bool errout; T a,b,c;
/**/ ((errout ? cerr : cout).flush()) << a << b << c;
( except where prohibited by coding standards or common sense ;) )
Regards,
Michiel Salters
---
[ 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 ]