How Come a Non-Const Reference Cannot Bind to a Temporary Object

How come a non-const reference cannot bind to a temporary object?

From this Visual C++ blog article about rvalue references:

... C++ doesn't want you to accidentally
modify temporaries, but directly
calling a non-const member function on
a modifiable rvalue is explicit, so
it's allowed ...

Basically, you shouldn't try to modify temporaries for the very reason that they are temporary objects and will die any moment now. The reason you are allowed to call non-const methods is that, well, you are welcome to do some "stupid" things as long as you know what you are doing and you are explicit about it (like, using reinterpret_cast). But if you bind a temporary to a non-const reference, you can keep passing it around "forever" just to have your manipulation of the object disappear, because somewhere along the way you completely forgot this was a temporary.

If I were you, I would rethink the design of my functions. Why is g() accepting reference, does it modify the parameter? If no, make it const reference, if yes, why do you try to pass temporary to it, don't you care it's a temporary you are modifying? Why is getx() returning temporary anyway? If you share with us your real scenario and what you are trying to accomplish, you may get some good suggestions on how to do it.

Going against the language and fooling the compiler rarely solves problems - usually it creates problems.



Edit: Addressing questions in comment:
1) `X& x = getx().ref(); // OK when will x die?` - I don't know and I don't care, because this is exactly what I mean by "going against the language". The language says "temporaries die at the end of the statement, unless they are bound to const reference, in which case they die when the reference goes out of scope". Applying that rule, it seems x is already dead at the beginning of the next statement, since it's not bound to const reference (the compiler doesn't know what ref() returns). This is just a guess however.

  1. I stated the purpose clearly: you are not allowed to modify temporaries, because it just does not make sense (ignoring C++0x rvalue references). The question "then why am I allowed to call non-const members?" is a good one, but I don't have better answer than the one I already stated above.

  2. Well, if I'm right about x in X& x = getx().ref(); dying at the end of the statement, the problems are obvious.

Anyway, based on your question and comments I don't think even these extra answers will satisfy you. Here is a final attempt/summary: The C++ committee decided it doesn't make sense to modify temporaries, therefore, they disallowed binding to non-const references. May be some compiler implementation or historic issues were also involved, I don't know. Then, some specific case emerged, and it was decided that against all odds, they will still allow direct modification through calling non-const method. But that's an exception - you are generally not allowed to modify temporaries. Yes, C++ is often that weird.

non-const lvalue reference to type '' cannot bind to a temporary of type ' *'

At least use the const qualifier

void addMyclass( const myclass& myclassobject){
mMyQueue->push(myclassobject);
}

Or overload the function like

void addMyclass(myclass&& myclassobject){
mMyQueue->push(myclassobject);
}

And instead of

mQueuetest->addMyclass(new myclass(...));

use

mQueuetest->addMyclass(myclass(...));

because the queue is declared as storing objects of the type myclass instead of pointers to objects.

queue<myclass>* mMyQueue = ...
^^^^^^^

Also it is unclear why you are using a pointer to std::queue as a data member.

queue<myclass>* mMyQueue = new queue<myclass>;

Just declare the data member like

queue<myclass> mMyQueue;

Here is a demonstrative program.

#include <iostream>
#include <queue>

struct myclass
{
};

class Queuetest
{
private:
std::queue<myclass> mMyQueue;

public:
void addMyclass( const myclass &myclassobject )
{
std::cout<< "void addMyclass( const myclass &myclassobject )\n";
mMyQueue.push(myclassobject);
}

void addMyclass( myclass &&myclassobject )
{
std::cout<< "void addMyclass( myclass &&myclassobject )\n";
mMyQueue.push(myclassobject);
}
};

int main()
{
Queuetest test;

test.addMyclass( myclass() );

myclass m;

test.addMyclass( m );
}

Its output is

oid addMyclass( myclass &&myclassobject )
void addMyclass( const myclass &myclassobject )

Error when casting temporary object to non-const reference

Yes, I think that is a bug in libc++.

According to [ostream.rvalue] there is an overload:

template<class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);

But libc++ implements this similar to:

template <class _Stream, class _Tp>
enable_if_t</*...*/, _Stream&&>
operator<<(_Stream&& __os, const _Tp& __x)
{
// ...
}

The implementation makes this overload a better candidate than the in-class operator<< of ostream in overload resolution if a stream rvalue is used with <<, while the signature in the standard wouldn't be, and it also returns a rvalue reference, when it should return a lvalue reference. It also returns a reference of same type as was passed to it, while it should return a reference to its ostream base class, according to the standard quote.

The rvalue reference can not be bound to a non-const lvalue reference, hence the error.

The bug has already been reported here and there is an open LWG issue regarding the behavior here, which seems to suggest that in the future the standard might be adjusted to mandate libc++'s current behavior.

How do I pass a temporary object as a non-const reference into a member function?

As mentioned in the comments, you could simply change your doSomething function to accept an rvalue reference to the passed XferInfo object (using the double &&):

    void doSomething(const string& somethingParam, XferInfo&& xferInfo)
{
// complete data for xfer
xferInfo.setCalleeInfo(somethingParam);
// ... and so forth ...

From the linked cppreference page:

Rvalue references can be used to extend the lifetimes of temporary
objects (note, lvalue references to const can extend the lifetimes of
temporary objects too, but they are not modifiable through them)

non-const lvalue reference to type ... cannot bind to a temporary of type [duplicate]

You can't.

The C++ standard does not allow the binding of an anonymous temporary to a reference, although some compilers allow it as an extension. (Binding to a const reference is allowed.)

Aside from the workaround you already have, if you can change the function to take const QImage& then that would be better.

why can temporary objects be bound to const reference?

If the object is temporary, why would making the reference const have any effect on the lifetime of the temporary object?

In the present context, the issue is not the lifetime of the object but whether you can modify it.

Say you make a call.

foo(10);

The object that holds the value 10 in the call should not be modified by the function. If the interface of foo is:

void foo(int& ref);

it's fair to implement foo as:

void foo(int& ref)
{
ref = 20;
}

That would be a problem given the call foo(10). It won't be a problem if foo uses a const&.

void foo(int const& ref)
{
ref = 20; // Not allowed.
}

From C++11 Standard, Temporary Objects/1

Temporaries of class type are created in various contexts: binding a reference to a prvalue ([dcl.init.ref]), returning a prvalue ([stmt.return]), a conversion that creates a prvalue, ...

and from C++11 Standard, References/5.2:

-- Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

A temporary can only bind to a reference to a prvalue. The type of such a reference must be a const qualified lvalue reference or a rvalue references.

MS Visual Studio compilers have allowed binding of non-const references to temporary objects but it is not sanctioned by the standard.

Why can an rvalue not bind to a non-const lvalue reference, other than the fact that writing to a temporary has no effect?

The simple answer is that in most cases, passing a temporary to a function that expects a mutable lvalue reference indicates a logic error, and the c++ language is doing its best to help you avoid making the error.

The function declaration: void foo(Bar& b) suggests the following narrative:

foo takes a reference to a Bar, b, which it will modify. b is therefore both an input and an output

Passing a temporary as the output placeholder is normally a much worse logic error than calling a function which returns an object, only to discard the object unexamined.

For example:

Bar foo();

void test()
{
/*auto x =*/ foo(); // probable logic error - discarding return value unexamined
}

However, in these two versions, there is no problem:

void foo(Bar&& b)

foo takes ownership of the object referenced by Bar

void foo(Bar b)

foo conceptually takes a copy of a Bar, although in many cases the compiler will decide that creating and copying a Bar is un-necessary.

So the question is, what are we trying to achieve? If we just need a Bar on which to work we can use the Bar&& b or Bar b versions.

If we want to maybe use a temporary and maybe use an existing Bar, then it is likely that we would need two overloads of foo, because they would be semantically subtly different:

void foo(Bar& b);    // I will modify the object referenced by b

void foo(Bar&& b); // I will *steal* the object referenced by b

void foo(Bar b); // I will copy your Bar and use mine, thanks

If we need this optionality, we can create it by wrapping one in the other:

void foo(Bar& b)
{
auto x = consult_some_value_in(b);
auto y = from_some_other_source();
modify_in_some_way(b, x * y);
}

void foo(Bar&& b)
{
// at this point, the caller has lost interest in b, because he passed
// an rvalue-reference. And you can't do that by accident.

// rvalues always decay into lvalues when named
// so here we're calling foo(Bar&)

foo(b);

// b is about to be 'discarded' or destroyed, depending on what happened at the call site
// so we should at lease use it first
std::cout << "the result is: " << v.to_string() << std::endl;
}

With these definitions, these are now all legal:

void test()
{
Bar b;
foo(b); // call foo(Bar&)

foo(Bar()); // call foo(Bar&&)

foo(std::move(b)); // call foo(Bar&&)
// at which point we know that since we moved b, we should only assign to it
// or leave it alone.
}

OK, by why all this care? Why would it be a logic error to modify a temporary without meaning to?

Well, imagine this:

Bar& foo(Bar& b)
{
modify(b);
return b;
}

And we're expecting to do things like this:

extern void baz(Bar& b);

Bar b;
baz(foo(b));

Now imagine this could compile:

auto& br = foo(Bar());

baz(br); // BOOM! br is now a dangling reference. The Bar no longer exists

Because we are forced to handle the temporary properly in a special overload of foo, the author of foo can be confident that this mistake will never happen in your code.



Related Topics



Leave a reply



Submit