strange compiler message

D

Denis Remezov

Dave said:
You need to post the code to know exactly why, but I'd
guess that you are passing a temporary object like Foo(),
as a parameter to a function which is a non const parameter,

ie, void foo( Foo& f ) instead of void foo( const Foo& f )

and you are doing f( Foo() ).

The standard doesn't allow you to modify tempoary objects like Foo(), so
that is why the compiler is griping.

Nitpicking:

You meant it doesn't allow a non-constant reference to be bound
to a temporary.

(It does allow you to modify non-const temporary objects of user-defined
types, since you can call member functions on such objects).

Denis
 
N

Neil Zanella

Hello,

I wonder whether anyone has ever come across
the following g++ compiler error message. I
don't recall ever seeing it before. I solved
my problem but I am still not sure about
what this message is all about. Any
ideas?

error: invalid initialization of non-const reference of
type 'Foo&' from a temporary of type 'Foo'

Thanks,

Neil
 
D

Dave Townsend

You need to post the code to know exactly why, but I'd
guess that you are passing a temporary object like Foo(),
as a parameter to a function which is a non const parameter,

ie, void foo( Foo& f ) instead of void foo( const Foo& f )

and you are doing f( Foo() ).

The standard doesn't allow you to modify tempoary objects like Foo(), so
that is why the compiler is griping.

dave
 
V

Victor Bazarov

Denis Remezov said:
Nitpicking:

You meant it doesn't allow a non-constant reference to be bound
to a temporary.

(It does allow you to modify non-const temporary objects of user-defined
types, since you can call member functions on such objects).

Nitpicking:

(It does allow you to modify temporary objects of user-defined types
since you can call non-const member function on such objects).

Victor
 
N

Neil Zanella

Dave Townsend said:
The standard doesn't allow you to modify tempoary objects like Foo(), so
that is why the compiler is griping.

Interesting, but I see no reason why the standard should forbid such
statements. Could you please elaborate on why that would be? Both
variables in the main() given below have automatic storage duration.
The only difference that I can see is that one has a name and the
other does not. So what do you mean by "temporary".


#include <iostream>

class Foo {
public:
Foo(int x): x(x) { }
void foo() const { std::cout << x << std::endl; }
private:
int x;
};

void foo(Foo &foo) { foo.foo(); }

void bar(const Foo &foo) { foo.foo(); }

int main() {

// disallowed: (why???)

foo(Foo(8)); // generates compile time error message

// allowed:

Foo x(8);
foo(x);

// syntax error: I guess the standard does not allow
// variables to be defined inside the arguments of
// function calls with a name, which makes sense,
// since where would that name be used, its scope
// being limited...
// foo(Foo y(88)); // syntax error

}
 
V

Victor Bazarov

I don't know why the compiler was emitting the message, but it is
a fact that temporary objects are _not_ constant and you _may_ modify
them at will (by calling member functions that are non-const). The
Standard never disallowed that, IIRC.
Interesting, but I see no reason why the standard should forbid such
statements. Could you please elaborate on why that would be? Both
variables in the main() given below have automatic storage duration.
The only difference that I can see is that one has a name and the
other does not. So what do you mean by "temporary".


#include <iostream>

class Foo {
public:
Foo(int x): x(x) { }
void foo() const { std::cout << x << std::endl; }
private:
int x;
};

void foo(Foo &foo) { foo.foo(); }

void bar(const Foo &foo) { foo.foo(); }

int main() {

// disallowed: (why???)

foo(Foo(8)); // generates compile time error message

Because it requires binding of a non-const reference to a temporary.
_That_ is not allowed.
// allowed:

Foo x(8);
foo(x);

Here 'x' is a real object (not temporary) and a non-const reference
can be bound to it without a problem.
// syntax error: I guess the standard does not allow
// variables to be defined inside the arguments of
// function calls with a name, which makes sense,
// since where would that name be used, its scope
// being limited...
// foo(Foo y(88)); // syntax error

Correct. There are few places where a declaration is accepted and
the list of arguments in a function call is _not_ one of them.

Victor
 
J

John Harrison

Interesting, but I see no reason why the standard should forbid such
statements.

Dave is incorrect. Tempoary objects can be modified. What is not allowed
to to bind a non-const reference to a temporary object.

Here is some code which modifies a temporary object

class X
{
X() : x(0) {}
void modify() { x = 1; }
};

int main()
{
X().modify();
}

The reason that binding a non-const reference to a temporary is not
allowed is that it was thought to be too potentially confusing.

have a look here for instance to see what BS had to say about this

http://tinyurl.com/6472t

john
 
?

=?ISO-8859-1?Q?Tobias_G=FCntner?=

Neil said:
#include <iostream>

class Foo {
public:
Foo(int x): x(x) { }
void foo() const { std::cout << x << std::endl; }
private:
int x;
};

void foo(Foo &foo) { foo.foo(); }

void bar(const Foo &foo) { foo.foo(); }

int main() {

// disallowed: (why???)

foo(Foo(8)); // generates compile time error message

You forgot the most important one:

int i = 8;
foo(i); // equal to foo(Foo(8)) but shows the problem more clearly

Given that foo(Foo&) actually modifies the passed object, a naive user
would expect foo() to modifiy i in the given example. Instead foo()
modifies a temporary Foo object and leaves i untouched.

void foo(int& r) { r = 10; }
class Foo { operator int() { ... } } myfoo;
foo(myfoo);

The same problem occurs in this case: foo modifies a temporary int - not
myfoo, as the user expects.

This would be very confusing and a source of very suble bugs, so it's
good that it's not allowed to bind a temporary object to a reference to
non-const.
 
N

Neil Zanella

Tobias Güntner said:
The same problem occurs in this case: foo modifies a temporary int - not
myfoo, as the user expects.

How could this be the problem? It's obvious from the signature of method
foo(int &) that myfoo will not be modified because it is not an int but
an object of class Foo. How could this possibly confuse anyone? There
must be a better reason why the standard disallows passing a temporary
to a function that takes a non-const reference. What I find really
strange is that since the object passed to such a function is a
temporary, I can't see that anyone would care about whether it
gets modified or not: since the object is temporary, the
programmer will never have a chance of accessing it ever
again, so why would the programmer care, and why would
the standard disallow it, given that it makes no
difference whether the temporary is modified or
not: after all, it's just a temporary.

Regards,

Neil


class X {
public:
X(): x(0) {}
void modify() { x = 1; }
private:
int x;
};

void foo(int& r) { r = 10; }

class Foo {
public:
operator int() { return 0; }
} myfoo;

int main() {
X().modify();
//foo(myfoo);
}
 
?

=?ISO-8859-1?Q?Tobias_G=FCntner?=

Neil said:
How could this be the problem? It's obvious from the signature of
method foo(int &) that myfoo will not be modified because it is not
an int but an object of class Foo. How could this possibly confuse
anyone?

Because the user/programmer does not see the functions signature at
first sight or the programmer has to make a few
last-minute-bugfixes and has no time to check the signatures. Or maybe
the programmer is just sloppy. One never knows ;)

Here's another example (there was a link to it in this thread):

void trim_whitespace(string& str);
int main()
{
trim_whitespace(" blah blah ");
return 0;
}

Now show me someone who would not be confused because the string literal
won't be modified. Or what if a user defined class provided a conversion
to string? trim_whitespace would not modify the object that has been
passed as parameter (at least from the user's point of view - the
compiler of course knows about any implicit conversions)

The point is: Because implicit conversions are performed, you might
*involuntaryly* pass a temporary object to a function that is supposed
to modify the original object.

If you're still in doubt, maybe this
There must be a better reason why the standard disallows passing a
temporary to a function that takes a non-const reference.
What I find really strange is that since the object passed to such a
function is a temporary, I can't see that anyone would care about
whether it gets modified or not:

Usually you pass objects to a modifying function because you really want
it to modify an object, don't you?
since the object is temporary, the programmer will never have a
chance of accessing it ever again

Exactly! Now tell me: Why should I call that function if I never use its
results? (and I'm not talking about error return codes here)
Why call a function that has absolutely no side effects? (or more
exactly: whose side effects vanish immediately after the function has
returned?)
so why would the programmer care, and why would the standard disallow
it, given that it makes no difference whether the temporary is
modified or not: after all, it's just a temporary.

Because in 99 out of 100 cases you don't want to modify a temporary. In
most cases you care for the result, otherwise you wouldn't call that
function.
class X { public: X(): x(0) {} void modify() { x = 1; } private: int
x; };
int main() { X().modify();

That's OK (i.e. this is the remaining 1 out of 100 cases), because you
consciously & deliberately created a temporary object here - no danger
of subtle bugs in this case.
 
N

Neil Zanella

Tobias Güntner said:
Because the user/programmer does not see the functions signature at
first sight or the programmer has to make a few
last-minute-bugfixes and has no time to check the signatures. Or maybe
the programmer is just sloppy. One never knows ;)

Here's another example (there was a link to it in this thread):

void trim_whitespace(string& str);
int main()
{
trim_whitespace(" blah blah ");
return 0;
}

Now show me someone who would not be confused because the string literal
won't be modified. Or what if a user defined class provided a conversion
to string? trim_whitespace would not modify the object that has been
passed as parameter (at least from the user's point of view - the
compiler of course knows about any implicit conversions)

Not confusing: in C and C++ it is illegal to modify string literals, thus,
even if the programmer were sloppy enough not to check the function signature,
the programmer would still know that " blah blah " would not get modified.

Furthermore, your example is not a good one because the following code is
legal according to the C++ standard, but according to your point of view
still more confusing than the illegal code you posted: here we have
a pointer to dynamically allocated memory. If the programmer doesn't
check the function signature, then, according to your point of view,
the programmer might think trim_whitespace() may call realloc on
blah (which it doesn't, since it takes an str, hence must call
construct a separate string first).

one is legal,
and yet, while still clear, more confusing than the one

void trim_whitespace(string& str);
int main()
{
char *blah = malloc(12);
strcpy(blah, " blah blah ");
trim_whitespace(blah);
return 0;
}
The point is: Because implicit conversions are performed, you might
*involuntaryly* pass a temporary object to a function that is supposed
to modify the original object.

So according to your point of view, why is the modified
I posted allowed by C++? All you said applies to the
modified code snippet I posted as well.
Usually you pass objects to a modifying function because you really want
it to modify an object, don't you?

Not true. Think about OpenGL where you must necessarily use globals to
save and restore state. Also, think about programs where you pass in an
object and the function returns another one (instead of being a void one
like the one in the example I gave). Think of all those functions that
take CONST references. There is no usually: it depends.
Exactly! Now tell me: Why should I call that function if I never use its
results?

Because we often don't need to modify what we pass in or use it again at all:

void foo(const int i) {
if (i == 0)
std::cout << "hello" << std::endl;
else
std::cout << "good night" << std::endl;
}

int main() {
foo(0);
foo(1);
}
(and I'm not talking about error return codes here)
Why call a function that has absolutely no side effects? (or more
exactly: whose side effects vanish immediately after the function has
returned?)

??? Obviously, you have narrowed your view to a particular class of functions.
Because in 99 out of 100 cases you don't want to modify a temporary. In
most cases you care for the result, otherwise you wouldn't call that
function.

IMHO this example doesn't even do anything... but it's just a particular case.
 
?

=?ISO-8859-1?Q?Tobias_G=FCntner?=

Neil said:
Not confusing: in C and C++ it is illegal to modify string literals, thus,
even if the programmer were sloppy enough not to check the function signature,
the programmer would still know that " blah blah " would not get modified.

That doesn't really matter... This literal could be hidden through some
preprocessor variable, a const buried in some headers or a return value
from some other function. It's just one out of a million comparable cases.
Fortunately the compiler will detect the error, no matter why the
programmer wrote such code (maybe he/she is a beginner and doesn't know
better?)
Furthermore, your example is not a good one because the following code is
legal according to the C++ standard,

no, it's not. see below.
but according to your point of view
still more confusing than the illegal code you posted: here we have
a pointer to dynamically allocated memory. If the programmer doesn't
check the function signature, then, according to your point of view,
the programmer might think trim_whitespace() may call realloc on
blah (which it doesn't, since it takes an str, hence must call
construct a separate string first).

That's what I'm trying to explain. And there are many more cases where
the error is not so obvious. Again: Because implicit conversions are
performed, even you might involuntaryly write such code. If you really
want the "wrong" (or "unobvious") behavior, you can still explicitly say so.
void trim_whitespace(string& str);
int main()
{
char *blah = malloc(12);
strcpy(blah, " blah blah ");
trim_whitespace(blah);
return 0;
}

This code is illegal and should not compile for two reasons:
1) malloc returns void* which cannot be converted to char* (but I guess
it's just a typo)
2) in the call to trim_whitespace, a temporary std::string has to be
constructed and bound to a reference to non-const string. Constructing a
string is allowed, but binding the temporary string to a reference to
non-const is not (see 13.3.3.1.4/3).
Actually the standard also says that references to non-const cannot be
bound to r-values [13.3.2/3], but IMHO that's more or less the same in
our context (although I'm not a language lawyer ;) )
So according to your point of view, why is the modified
I posted allowed by C++? All you said applies to the
modified code snippet I posted as well.

As I said, it's _not_ allowed and you've provided a nice example that
shows why it isn't allowed ;)
Not true. Think about OpenGL where you must necessarily use globals to
save and restore state. Also, think about programs where you pass in an
object and the function returns another one (instead of being a void one
like the one in the example I gave). Think of all those functions that
take CONST references. There is no usually: it depends.

You've lost me here... What do you mean by this? That const-correctness
is not as widely spread as it should be?
Because we often don't need to modify what we pass in or use it again at all:

void foo(const int i) {
if (i == 0)
std::cout << "hello" << std::endl;
else
std::cout << "good night" << std::endl;
}

int main() {
foo(0);
foo(1);
}

I was talking about the case where foo takes an int&

void calc(int& result) {
result = /* some calculation */;
// the only visible effects of calc() are
// the effects caused to the result parameter
// calc does not modify any global variables,
// do I/O, etc. or call other functions that might do
}

int main()
{
// let calc() do something, but I don't care for the result
// If the compiler is smart enough, it will optimize the
// entire function call away - What has been gained by
// this function call? Why did the programmer do this?
calc(int());

// some class that provides operator int()
convertible_to_int not_an_int;
// ...
calc(not_an_int);
// not_an_int is unchanged - is this really the intent?
// Did the programmer really want to write a do-nothing statement?
}
??? Obviously, you have narrowed your view to a particular class of functions.

because IMHO the restriction has been placed to prevent subtle bugs that
can only occur under very specific circumstances.

There is no problem with functions that take references to const - It
clearly states "dear function, take this object and do with it what you
like, but don't change it", whereas reference to non-const says "take
this object and do with it what you like; you may even change it".
In the latter case, the compiler cannot be sure if the programmer wanted
to modify a) the original object or b) the temporary object that had to
be created for conversion. These two things are completely different!
 
N

Neil Zanella

Tobias Güntner said:
This code is illegal and should not compile for two reasons:
1) malloc returns void* which cannot be converted to char* (but I guess
it's just a typo)

You're right, this in fact seems to be one difference between C and C++:

In C you can say "char *blah = malloc(12);",
but in C++ you must explicitly use a cast or
the compiler will complain.
2) in the call to trim_whitespace, a temporary std::string has to be
constructed and bound to a reference to non-const string. Constructing a
string is allowed, but binding the temporary string to a reference to
non-const is not (see 13.3.3.1.4/3).
Actually the standard also says that references to non-const cannot be
bound to r-values [13.3.2/3], but IMHO that's more or less the same in
our context (although I'm not a language lawyer ;) )

I see what you mean now: consider the following code:

#include <iostream>
#include <cstring>
#include <string>

void foo(std::string &str) { // not allowed because C++ won't convert a
// temporary to a nonconst reference on a
// function call: must use the following
// signature instead
//void foo(const std::string &str) {
std::cout << str << std::endl;
}

int main() {
char *blah = static_cast<char *>(malloc(22));
strcpy(blah, " blah blah ");
foo(blah);
}

When I use the wrong signature the compiler issues the following error:

$ g++ hello.cpp
hello.cpp: In function `int main ()':
hello.cpp:16: could not convert `blah' to `string &'
hello.cpp:5: in passing argument 1 of `foo (string &)'
As I said, it's _not_ allowed and you've provided a nice example that
shows why it isn't allowed ;)

Thanks. ;)
because IMHO the restriction has been placed to prevent subtle bugs that
can only occur under very specific circumstances.

There is no problem with functions that take references to const - It
clearly states "dear function, take this object and do with it what you
like, but don't change it", whereas reference to non-const says "take
this object and do with it what you like; you may even change it".
In the latter case, the compiler cannot be sure if the programmer wanted
to modify a) the original object or b) the temporary object that had to
be created for conversion. These two things are completely different!

In other words it's all there to prevent programming errors due to the
programmer not checking signatures: to prevent the situation where the
programmer expects the passed in parameter to be modified when in fact
something constructed from it ends up being modified.

All of this because C++ introduces the concept of calling a constructor
on a parameter to a function. The fact that such constructor may
do a copy on the passed in argument means that the resulting
constructed object may not alter the origninal object when
acted upon by some code.

Fair enough. It seems like nothing more than a design decision.
The following is legal because it does not make use of the
"construction on function call" as described above.

#include <iostream>
#include <cstring>
#include <string>

void foo(std::string &str) {
std::cout << str << std::endl;
}

int main() {
char *blah = static_cast<char *>(malloc(22));
strcpy(blah, " blah blah ");
std::string s(blah);
foo(s);
}

The following is also disallowed, by design decision: the C++ designers
figured that since a temporary is being passed in good coding practice
dictates that the programmer ought to be make foo const. (?)

#include <iostream>
#include <cstring>
#include <string>

void foo(std::string &str) {
std::cout << str << std::endl;
}

int main() {
char *blah = static_cast<char *>(malloc(22));
strcpy(blah, " blah blah ");
foo(std::string(blah));
}
 
?

=?ISO-8859-1?Q?Tobias_G=FCntner?=

Neil said:
In other words it's all there to prevent programming errors due to the
programmer not checking signatures: to prevent the situation where the
programmer expects the passed in parameter to be modified when in fact
something constructed from it ends up being modified.

All of this because C++ introduces the concept of calling a constructor
on a parameter to a function. The fact that such constructor may
do a copy on the passed in argument means that the resulting
constructed object may not alter the origninal object when
acted upon by some code.

Exactly. Fortunately the compiler immediately reminds the programmer
that he/she tried to do something stupid ;)
Fair enough. It seems like nothing more than a design decision.

I agree. I don't see any technical difficulties that would lead to this
restriction.
The following is also disallowed, by design decision: the C++ designers
figured that since a temporary is being passed in good coding practice
dictates that the programmer ought to be make foo const. (?)

Indeed, that might be a hint that something is wrong with the program's
design.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,175
Messages
2,570,942
Members
47,489
Latest member
BrigidaD91

Latest Threads

Top