C4239 - why here?

  • Thread starter Thread starter Goran Pusic
  • Start date Start date
G

Goran Pusic

Hi all!

I understand the rationale behind the warning 4239 (I think :-))
(warning C4239: nonstandard extension used : 'argument' : conversion from X
to Y A reference that is not to 'const' cannot be bound to a non-lvalue;
assigment operator takes a reference to non-const)

However, in this situation:

void F(X& x)

I get it for code like this:

F(X());

And I find it perfectly fine. So I have to use pragma's or split this in two
lines like this:
{ X x;
F(x); }

(With brackets, x gest destroyed immediately after the call; without, it
goes out of scope later, which sometimes isn't good; but OK, this is not my
man point)

Anyhow... Yuck!

Anybody has a comment on this isue? (Especially this: what "const" has to do
with anything here!?)

Goran.
 
Goran said:
Hi all!

I understand the rationale behind the warning 4239 (I think :-))
(warning C4239: nonstandard extension used : 'argument' : conversion from X
to Y A reference that is not to 'const' cannot be bound to a non-lvalue;
assigment operator takes a reference to non-const)

However, in this situation:

void F(X& x)

I get it for code like this:

F(X());

And I find it perfectly fine. So I have to use pragma's or split this in two
lines like this:
{ X x;
F(x); }

(With brackets, x gest destroyed immediately after the call; without, it
goes out of scope later, which sometimes isn't good; but OK, this is not my
man point)

Anyhow... Yuck!

Anybody has a comment on this isue? (Especially this: what "const" has to do
with anything here!?)

Basically, there is a rule that a non-const reference cannot bind to an
rvalue (such as a temporary). e.g.

int i;
int*& aref = &i; //attempting to bind to an rvalue
int& bref = 5; //again, binding to an rvalue
and your example:
X& x = X(); //binding to an rvalue.

Microsoft just gives a warning, though most compilers will refuse to
compile the code entirely.

The reason for the rule is to prevent the loss of modifications to
variables. For example:

void f(int& i)
{
i = 10;
}

double d = 0;
f(d);
//would you expect d to be 10?

Were the binding allowed, d would be converted to a temporary int, and f
would modify that temporary int, leaving d unchanged.

However, there isn't really a good reason why the binding shouldn't be
allowed when you have an exact type match (no conversions required), as
in your X example, but I think it was decided way back that the special
case wasn't really worth it, given the simple workaround.

Tom
 
void f(int& i)
{
i = 10;
}

double d = 0;
f(d);
//would you expect d to be 10?

Yes, I see. In fact, i would expect the compiler to refuse this outright, it
just makes no sense at all! Using intrinsic type promotions on references
like this, tsk, tsk, naughty compiler... Pascal doesn't allow this! Does C#?
I hope not :-))

But, that's not my situation. I have X as a "polymorphic worker class", and
it may or not change inside X, I don't care. I just want to it passed to F
to get polymorphic behaviour in F depending on the calling context).
Sort-of:
BaseX
{ virtual f() }
X1:BaseX { overridden virtual F() }
X2, X3...
and then F(BaseX&) gets called like this: F(X1(params)), F(X2(params)),
F(X3(params)) etc...

Goran.
 
But, that's not my situation. I have X as a "polymorphic worker class", and
it may or not change inside X, I don't care. I just want to it passed to F
to get polymorphic behaviour in F depending on the calling context).
Sort-of:
BaseX
{ virtual f() }
X1:BaseX { overridden virtual F() }
X2, X3...
and then F(BaseX&) gets called like this: F(X1(params)), F(X2(params)),
F(X3(params)) etc...

I talked about this problem here and in the message referenced from 1997:

http://groups.google.com/group/microsoft.public.vc.stl/msg/0e2c9910ccae9bfd

The solution (kludge) I settled on was to make F take a spuriously const
X1&, which is fine unless the class stores a reference or pointer to the
object. The problem with a const X& parameter then is that it's very easy
to slip up and pass a temporary, which will be destroyed at the end of the
expression containing the function call, long before the class has finished
using it.

I once posted this as an example of a sort of built-in joke concerning the
whole rvalue/lvalue/reference behavior:

*****

http://groups.google.com/[email protected]

struct X
{
};

void g(X&);

void f()
{
X() = X();

g(X() = X());

g(X()); // No good
}

Note that above, the result of assigning one rvalue to another rvalue
is a modifiable lvalue. :)

*****

The difference between the rvalues produced by X() and int() is that the
former has member functions you can call on it, and you're allowed to call
them, including the assignment operator, which by default returns a
reference to the object assigned.
 
Goran said:
Yes, I see. In fact, i would expect the compiler to refuse this outright, it
just makes no sense at all! Using intrinsic type promotions on references
like this, tsk, tsk, naughty compiler... Pascal doesn't allow this! Does C#?
I hope not :-))

Well, VC++ does at least give a diagnostic to indicate that the code has
an error - the code is "ill-formed" C++, and VC++ is reporting that fact.
But, that's not my situation. I have X as a "polymorphic worker class", and
it may or not change inside X, I don't care. I just want to it passed to F
to get polymorphic behaviour in F depending on the calling context).
Sort-of:
BaseX
{ virtual f() }
X1:BaseX { overridden virtual F() }
X2, X3...
and then F(BaseX&) gets called like this: F(X1(params)), F(X2(params)),
F(X3(params)) etc...

Perhaps F should take a const reference and f() should be a const
member? Alternatively, there is this workaround that must be used with care:

template <class T>
inline T& ref_from_temp(T const& t)
{
return const_cast<T&>(t);
}

F(ref_from_temp(X1(params)));

Tom
 
Back
Top