Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

non-const reference to temporary

236 views
Skip to first unread message

Walter...@gmail.com

unread,
Aug 15, 2008, 10:10:37 PM8/15/08
to
I have a disagreement with this "error". I'm constantly running into
this with g++, perhaps, hoping at some point the latest version has
changed this. For example, here's a function.

void WriteEverythingToFile( File& file, const std::string& text)
{
file.write(text);
}

The "File" can't be const in this case, for example, because in this
class, the write operation alters some data members.

Now say I just want to quickly write some stuff to a file and I don't
care about the file object persisting or leaving any extraneous
temporary variables lying around.

WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
goes into the file"));

g++ will tell me this is an error because I'm passing a non const
reference to a temporary (anonymous variable with no name).

First of all this isn't an error, except by someones overly broad
definition of error. Other people say "why would you pass a non-const
reference to something which will be modified, but you don't care
about the modification?".

Exactly. I don't care about the modification. I just want the function
to do the job I request.

g++ doesn't complain about this type of construct:
File("afilename").write("this is some text"); which has all the same
criticisms, "Why are you creating an non-const object, if you don't
save it to a variable?". Exactly, I don't want to save it, I just want
to write to the file and be done with it, no extraneous variables
hanging around, no greasy kid stuff.

And please, don't say, "why don't you do it this way". I know how to
work around this. The problem is, I shouldn't have to.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Mathias Gaunard

unread,
Aug 16, 2008, 7:52:43 PM8/16/08
to
On 16 août, 04:10, "WalterHow...@gmail.com" <WalterHow...@gmail.com>
wrote:

> WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> goes into the file"));
>
> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).

And it is.


> First of all this isn't an error, except by someones overly broad
> definition of error.

The C++ standard clearly says it is not allowed.
So it is an error.

There are several reasons why it was chosen to be so. Go back in time
and look into how C++ was designed.


> g++ doesn't complain about this type of construct:
> File("afilename").write("this is some text"); which has all the same
> criticisms, "Why are you creating an non-const object, if you don't
> save it to a variable?". Exactly, I don't want to save it, I just want
> to write to the file and be done with it, no extraneous variables
> hanging around, no greasy kid stuff.

While binding an rvalue to a non-const reference isn't allowed, an
rvalue does not have to be const.
This is therefore perfectly valid.

(Note that binding an rvalue to a const-reference may cause a copy,
and converts the rvalue into an lvalue)

Joshua...@gmail.com

unread,
Aug 16, 2008, 11:18:50 PM8/16/08
to
On Aug 15, 7:10 pm, "WalterHow...@gmail.com" <WalterHow...@gmail.com>
wrote:

Now, yelling like this and being purposefully argumentative is
probably never a good thing. It's going to make people less likely to
respond.

It is an error by the C++ standard. The C++ clearly states that
unnamed objects, a.k.a. temporaries, may be bound to const references
and may not be bound to non-const references.

Is this a desirable rule of the language? I imagine it prevents a lot
of bufs, but as you've noted, it can be quiet inconvenient at times.
As an example, read up on the history of auto_ptr and how it
repeatedly hit this issue in its design.

David Abrahams

unread,
Aug 16, 2008, 11:21:46 PM8/16/08
to

on Fri Aug 15 2008, "WalterHoward-AT-gmail.com" <WalterHoward-AT-gmail.com> wrote:

> I have a disagreement with this "error". I'm constantly running into
> this with g++, perhaps, hoping at some point the latest version has
> changed this.

g++ 4.3 (and the next C++ standard) has rvalue references, so, yes, it's
been changed.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

red floyd

unread,
Aug 16, 2008, 11:21:27 PM8/16/08
to
Walter...@gmail.com wrote:
> I have a disagreement with this "error". I'm constantly running into
> this with g++, perhaps, hoping at some point the latest version has
> changed this. For example, here's a function.
>
> void WriteEverythingToFile( File& file, const std::string& text)
> {
> file.write(text);
> }
>
> The "File" can't be const in this case, for example, because in this
> class, the write operation alters some data members.
>
> Now say I just want to quickly write some stuff to a file and I don't
> care about the file object persisting or leaving any extraneous
> temporary variables lying around.
>
> WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> goes into the file"));
>
> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).
>
> First of all this isn't an error, except by someones overly broad
> definition of error.

No. It's an error because the STANDARD -- ISO/IEC 14882:2003 -- says
it's an error, not because some guy on the g++ team though ti should be.

Deal with it.

Chris Morley

unread,
Aug 16, 2008, 11:24:09 PM8/16/08
to
> WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> goes into the file"));
>
> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).

No I wouldn't have though it was an error either. What is your File
constructor like?

Does this compile & run?

Chris

---- 8< -------- 8< -------- 8< -------- 8< -------- 8< -------- 8< ----

#include <string>
#include "stdio.h"
#include "conio.h"

class File {
public:
// File( const char* fn ) : filename(fn) {} //ok too
File( const std::string& fn ) : filename(fn) {}
~File() { display(); }

void write( const std::string& stuff ) { buf += stuff; }

void display();

private:
std::string filename;
std::string buf;
};

void File::display()
{
printf( "Filename: %s\n", filename.c_str() );
printf( "Contents:\n%s\n", buf.c_str() );
printf( "Display end...\n\n" );
}

void WriteEverythingToFile( File& file, const std::string& text)
{
file.write(text);
}

int main(int argc, char* argv[])
{


WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff goes
into the file"));

File("afilename").write("this is some text");

_getch();
return 0;

blargg

unread,
Aug 16, 2008, 11:22:32 PM8/16/08
to
In article
<d73726c4-60de-4702...@v16g2000prc.googlegroups.com>,
"Walter...@gmail.com" <Walter...@gmail.com> wrote:

> [...]


> void WriteEverythingToFile( File& file, const std::string& text)

> [...]


> WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> goes into the file"));
>
> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).

> [...]


> And please, don't say, "why don't you do it this way". I know how to
> work around this. The problem is, I shouldn't have to.

template<typename T>
inline T& nonconst( T const& t ) { return const_cast<T&> (t); }

WriteEverythingToFile( nonconst(File("AFileName.txt")),
"This stuff goes into the file" );

Just don't complain about the reduced compile-time checking.

Dave Harris

unread,
Aug 16, 2008, 11:27:04 PM8/16/08
to
Walter...@gmail.com () wrote (abridged):

> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).
>
> First of all this isn't an error, except by someones overly broad
> definition of error. Other people say "why would you pass a
> non-const reference to something which will be modified, but
> you don't care about the modification?".
>
> Exactly. I don't care about the modification. I just want the
> function to do the job I request.

I agree it shouldn't be an error. The reason it is treated as one is that
the construct is, in general, error-prone, because of implicit
conversions. Code like:

void inc( long &x ) { ++x; }

void test() {
int y = 0;
inc( y );
std::cout << y;
}

if it compiled, would output 0 not 1, because the int is converted to a
temporary long and the increment lost.

The problem here is the implicit conversion, not the reference to
temporary. Unfortunately, rather than try to ban non-const references to
temporaries that resulted from implicit conversions, the standard bans
non-const references to all temporaries, which is over-kill.

The proposed solution, apparently, is a new kind of reference, spelt "&&",
that can bind to temporaries. I think if your function has the signature:
void WriteEverythingToFile( File&& file, const std::string& text);

it will work in C++0x. But I'm not an expert on the new stuff, and it
doesn't help you now, nor will it unless you can rewrite the function you
need to call. The situation is less than ideal. You can see, though, that
there are real problems lurking here which are non-trivial to solve, and
the quote you attribute to "other people" doesn't do the problems
justice.

-- Dave Harris, Nottingham, UK.

Stuart Golodetz

unread,
Aug 17, 2008, 10:46:32 AM8/17/08
to

See:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm

Specifically the bit entitled "Binding temporaries to references".

Regards,
Stu

Erik Wikström

unread,
Aug 17, 2008, 11:28:32 AM8/17/08
to
On 2008-08-16 04:10, Walter...@gmail.com wrote:
> I have a disagreement with this "error". I'm constantly running into
> this with g++, perhaps, hoping at some point the latest version has
> changed this. For example, here's a function.
>
> void WriteEverythingToFile( File& file, const std::string& text)
> {
> file.write(text);
> }
>
> The "File" can't be const in this case, for example, because in this
> class, the write operation alters some data members.
>
> Now say I just want to quickly write some stuff to a file and I don't
> care about the file object persisting or leaving any extraneous
> temporary variables lying around.
>
> WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> goes into the file"));
>
> g++ will tell me this is an error because I'm passing a non const
> reference to a temporary (anonymous variable with no name).
>
> First of all this isn't an error, except by someones overly broad
> definition of error. Other people say "why would you pass a non-const
> reference to something which will be modified, but you don't care
> about the modification?".

It is an error because C++ does not allow passing a reference to a
temporary, and that's it. It might not be something you agree with and
in some cases it might not make much sense (like in your example). On
the other hand it is a good solution to the problem at large (if you
could create references to temporaries it could cause a lot of trouble
in other situations).

In short you can wait until hell freezes before gcc will support what
you want, or you can wait until gcc supports the C++0x standard and use
r-value references.

--
Erik Wikström

Message has been deleted

Howard Hinnant

unread,
Aug 17, 2008, 11:28:32 AM8/17/08
to
On Aug 15, 10:10 pm, "WalterHow...@gmail.com" <WalterHow...@gmail.com>
wrote:

There will be a new reference type in the next C++ standard (C++0X,
where X is hexadecimal :-() which allows you to do exactly what you
want. Search for "rvalue reference". If the author of
WriteEverythingToFile writes:

void WriteEverythingToFile( File&& file, const std::string& text)
{
file.write(text);
}

then rvalue File's will bind (note the extra '&'). You can experiment
with this new language feature today in a few compilers such as gcc
4.3 (with C++0X features turned on as a command line flag to the
compiler).

-Howard

Chris Morley

unread,
Aug 17, 2008, 12:58:35 PM8/17/08
to

"Chris Morley" <chris....@lineone.net> wrote in message
news:48a6864c$0$2925$fa0f...@news.zen.co.uk...

> > WriteEverythingToFile(File("AFileName.txt"), std::string("This stuff
> > goes into the file"));
> >
> > g++ will tell me this is an error because I'm passing a non const
> > reference to a temporary (anonymous variable with no name).
>
> No I wouldn't have though it was an error either. What is your File
> constructor like?
>
> Does this compile & run?

After reading other people's comments I tried it with gcc too because it
works fine on VC6 & VS2008. My example doesn't compile.

You can cast like blargg says but (if I had to and didn't use MS compilers)
I'd do it like this rather than embedded in the call...

void WriteEverythingToFile( const File& file, const std::string& text)
{
File& myfile = const_cast<File&>(file);
myfile.write(text);
}

or... use a pointer & ignore the warning the compiler gives about taking the
address of a temporary (thanks gcc, but I am a grown up).

void WriteEverythingToFile( File* file, const std::string& text)
{
file->write(text);
}

or.. you could make the write a const function too & make the members you
change mutable (or force the this to a (File*) with a const cast inside the
File function.

void write_const( const std::string& stuff ) const
{
File* nonconstthis = const_cast< File* >(this);
nonconstthis->buf += stuff;
}

Below does work with gcc.

I would tend to agree with yours & Dave Harris' sentiment about this error.
It wouldn't be too hard to fix, MS did it in VC6 (presumably apparently
non-standard) but still refuses to compile DH's long& example as it should.

Anyway, hope these workarounds help.

Chris

---- 8< -------- 8< -------- 8< -------- 8< -------- 8< -------- 8< ----

#include <string>
#include "stdio.h"

class File {
public:
//File( const char* fn ) : filename(fn) {}


File( const std::string& fn ) : filename(fn) {}
~File() { display(); }

void write( const std::string& stuff ) { buf += stuff; }

void write_const( const std::string& stuff ) const
{
File* nonconstthis = const_cast< File* >(this);
nonconstthis->buf += stuff;
}

void display();

private:
std::string filename;
std::string buf;
};

void File::display()
{
printf( "Filename: %s\n", filename.c_str() );
printf( "Contents:\n%s\n", buf.c_str() );
printf( "Display end...\n\n" );
}

void WriteEverythingToFile( const File& file, const std::string& text)
{
File& myfile = const_cast<File&>(file);
myfile.write(text);
}

void WETF_pointer( File* file, const std::string& text)
{
file->write(text);
}

void WETF_inclass( const File& file, const std::string& text)
{
file.write_const(text);
}

int main(int argc, char* argv[])
{

WriteEverythingToFile(File("reference.txt"), std::string("This stuff goes
into the file"));

WETF_pointer(&File("pointer.txt"), std::string("This pointer stuff goes
into the file"));
WETF_inclass(File("in_class.txt"), std::string("This stuff goes into the
file"));

File("afilename").write("this is some text");

return 0;

Dave Harris

unread,
Aug 18, 2008, 5:24:31 PM8/18/08
to
chris....@lineone.net (Chris Morley) wrote (abridged):

> You can cast like blargg says but (if I had to and didn't use MS
> compilers) I'd do it like this rather than embedded in the call...
>
> void WriteEverythingToFile( const File& file, const std::string&
> text)
> {
> File& myfile = const_cast<File&>(file);
> myfile.write(text);
> }

An alternative is to just overload the function to take a filename:

void WriteEverythingToFile( File &file, const std::string &text );

void WriteEverythingToFile( const char *name, const std::string &text ) {
File myfile( name );
WriteEverythingToFile( myFile, text );
}

Now you can call with:
WriteEverythingToFile( "filename.txt",
std::string("stuff to write") );

which makes the short case even shorter, and doesn't involve unsafe
const_casts, mutable etc.


> or... use a pointer & ignore the warning the compiler gives about
> taking the address of a temporary (thanks gcc, but I am a grown up).

You can't take the address of a temporary in standard C++. (I forget
exactly why - the rule predates the rule about non-const references and
goes back to plain C, where temporaries were stored in registers and had
no address.)


> I would tend to agree with yours & Dave Harris' sentiment about
> this error. It wouldn't be too hard to fix, MS did it in VC6
> (presumably apparently non-standard) but still refuses to
> compile DH's long& example as it should.

Um, I didn't quite say it should be easy to fix. I said "there are real
problems lurking here which are non-trivial to solve".

-- Dave Harris, Nottingham, UK.

--

Chris Morley

unread,
Aug 18, 2008, 8:19:29 PM8/18/08
to
> An alternative is to just overload the function to take a filename:
>
> void WriteEverythingToFile( File &file, const std::string &text );
>
> void WriteEverythingToFile( const char *name, const std::string &text ) {
> File myfile( name );
> WriteEverythingToFile( myFile, text );
> }
>
> Now you can call with:
> WriteEverythingToFile( "filename.txt",
> std::string("stuff to write") );
>
> which makes the short case even shorter, and doesn't involve unsafe
> const_casts, mutable etc.

Quite so.

> > or... use a pointer & ignore the warning the compiler gives about
> > taking the address of a temporary (thanks gcc, but I am a grown up).
>
> You can't take the address of a temporary in standard C++. (I forget
> exactly why - the rule predates the rule about non-const references and
> goes back to plain C, where temporaries were stored in registers and had
> no address.)

You can with GCC (& MS's compilers) so even if it shouldn't be allowed it is
something you can do with gcc & it works. My example builds with gcc and
executes as expected. It just warns you about it because you might do
something dumb like keep the pointer around after the temp has gone out of
scope and been destroyed and try to use it again.

> Um, I didn't quite say it should be easy to fix. I said "there are real
> problems lurking here which are non-trivial to solve".

No, you didn't say it is easy to fix but I think it is. The fact is that
MS's compilers from VC6 -> VS2008 already allow const reference to a
temporary but reject your long& type conversion example code. Walter's
original method works the "intuative" way with these compilers.

Chris

ta0...@yahoo.com

unread,
Aug 21, 2008, 8:58:35 AM8/21/08
to
On Aug 15, 10:10 pm, "WalterHow...@gmail.com" <WalterHow...@gmail.com>
wrote:
> I have a disagreement with this "error". I'm constantly running into
> this with g++, perhaps, hoping at some point the latest version has
> changed this. For example, here's a function.

An lvalue requires a valid memory location such as stack or heap
memory so that it can be assignable. An rvalue has no such
requirement, so it can technically reside in a register, protected
memory (such as string constants,) the actual executable file (such as
POD literals,) etc. You can get around your problem (if you control
the class) by adding e.g. 'operator File&()' in the class 'File' that
just returns '*this'.
Kevin P. Barry

Walter...@gmail.com

unread,
Sep 3, 2008, 4:54:22 PM9/3/08
to
> It is an error because C++ does not allow passing a reference to a
> temporary, and that's it. It might not be something you agree with and
> in some cases it might not make much sense (like in your example). On
> the other hand it is a good solution to the problem at large (if you
> could create references to temporaries it could cause a lot of trouble
> in other situations).
>
> In short you can wait until hell freezes before gcc will support what
> you want, or you can wait until gcc supports the C++0x standard and use
> r-value references.

I don't think the problem is "references to temporaries" because it
allows you to bind a reference to a const temporary. And in this
situation, where the reference is a function parameter, you are
guaranteed that the temporary won't be destructed out from under you
if you refer to it within the function. In any case, the danger is no
different than if the reference were const. As to arguments about the
underlying implementation by the compiler writers, if I can cast away
the const of the parameter within the function, and that works, what's
the real difference between const and non-const in the parameter?

I understand the standard says what it says. We make progress by
challenging the standards. I'm very happy with the way C++ was
conceived, evolved and implemented. It's the only language I'm happy
with.

If the new standard lets me do this without casting away const, I'm
happy. I guess enough people saw a need to support this situation.


--

Seungbeom Kim

unread,
Sep 4, 2008, 5:29:09 AM9/4/08
to
Dave Harris wrote:
> chris....@lineone.net (Chris Morley) wrote (abridged):
>> You can cast like blargg says but (if I had to and didn't use MS
>> compilers) I'd do it like this rather than embedded in the call...
>>
>> void WriteEverythingToFile( const File& file, const std::string&
>> text)
>> {
>> File& myfile = const_cast<File&>(file);
>> myfile.write(text);
>> }
>
> An alternative is to just overload the function to take a filename:
>
> void WriteEverythingToFile( File &file, const std::string &text );
>
> void WriteEverythingToFile( const char *name, const std::string &text ) {
> File myfile( name );
> WriteEverythingToFile( myFile, text );
> }
>
> Now you can call with:
> WriteEverythingToFile( "filename.txt",
> std::string("stuff to write") );
>
> which makes the short case even shorter, and doesn't involve unsafe
> const_casts, mutable etc.

That's not always possible or desirable; in fact, I often find it
much more flexible to have the subroutine accept a stream reference
parameter (std::istream& or std::ostream&) instead of a filename,
because that allows the subroutine to work on any kind of streams.
A typical pattern occurring very often looks like this:

void subroutine(std::istream&);

int main(int argc, char** argv)
{
if (argc < 2)
subroutine(std::cin);
else while (++argv, --argc) {
if (std::strcmp(*argv, "-") == 0)
subroutine(std::cin);
else {
std::ifstream f(*argv);
if (f)
subroutine(f);
else
std::cerr << *argv << ": " << strerror(errno) << '\n';
}
}
}

where you can call subroutine() with either a std::ifstream object
or std::cin.

--
Seungbeom Kim

Dave Harris

unread,
Sep 5, 2008, 5:38:18 AM9/5/08
to
musi...@bawi.org (Seungbeom Kim) wrote (abridged):

> Dave Harris wrote:
> > An alternative is to just overload the function to take a
> > filename:
>
> That's not always possible or desirable; in fact, I often find it
> much more flexible to have the subroutine accept a stream reference
> parameter (std::istream& or std::ostream&) instead of a filename,
> because that allows the subroutine to work on any kind of streams.

Sure, but with overloading you can accept either type of argument.

Chris Morley's suggestion had already added an overload and I think it's
better to use one that does directly what you want (ie lets you use a
name instead of a File) and avoids the dangerous const_cast. (It's
dangerous because you don't know locally whether the File was genuinely
const.)

-- Dave Harris, Nottingham, UK.

--

0 new messages