Can't Pass Temporary Object as Reference

Can't pass temporary object as reference

By design, C++ only allows a temporary to be passed to a const reference, value, or rvalue reference. The idea is that a function taking a non-const reference parameter is stating that it wants to modify the parameter and allowing it to go back to the caller. Doing so with a temporary is meaningless and most likely an error.

And I don't know what version of g++ you're running. It doesn't work here: http://coliru.stacked-crooked.com/a/43096cb398cbc973

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)

Passing temporary object as a reference to an abstract to a constructor

The lifetime of a temporary object like your Sub("") ends at the end of the full-expression in which it appears, i.e. at the semicolon.

C++ doesn't allow you to bind such temporary objects to mutable references, but Microsoft's compiler does allow that. Nonetheless, initializing a class member reference from such a temporary value immediately produces a dangling reference, which you must never ever use again; evaluating it outside the constructor body results in undefined behaviour.

The way to fix this is to not store references in your class!

For example, if you wanted ownership of a polymorphic value, you could use a unique pointer:

 #include <memory>

class User
{
std::unique_ptr<Super> s_;

public:
User(std::unique_ptr<Super> s) : s_(std::move(s)) {}

void work() { s_->func(); }
};

 User u(std::make_unique<Sub>("Hello"));
u.work();

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.

Why doesn't a const reference extend the life of a temporary object passed via a function?

It's by design. In a nutshell, only the named reference to which the temporary is bound directly will extend its lifetime.

[class.temporary]

5 There are three contexts in which temporaries are destroyed at a
different point than the end of the full-expression. [...]

6 The third context is when a reference is bound to a temporary.
The temporary to which the reference is bound or the temporary that is
the complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except:

  • A temporary object bound to a reference parameter in a function call persists until the completion of the full-expression containing
    the call.
  • The lifetime of a temporary bound to the returned value in a function return statement is not extended; the temporary is destroyed
    at the end of the full-expression in the return statement.
  • [...]

You didn't bind directly to ref2, and you even pass it via a return statement. The standard explicitly says it won't extend the lifetime. In part to make certain optimizations possible. But ultimately, because keeping track of which temporary should be extended when a reference is passed in and out of functions is intractable in general.

Since compilers may optimize aggressively on the assumption that your program exhibits no undefined behavior, you see a possible manifestation of that. Accessing a value outside its lifetime is undefined, this is what return ref2; does, and since the behavior is undefined, simply returning zero is a valid behavior to exhibit. No contract is broken by the compiler.

c++: what's the design philosophy of allowing temporary object to call non-const member function?

OK, I am putting some additional materials here. The following materials are quoted from David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor-C++ Templates_ The Complete Guide-Addison-Wesley (2017), C.2.1 The Implied Argument For Member Functions.

“An old special-case permits an rvalue to be bound to an lvalue
reference to non-const type when that reference is the traditional
implicit *this parameter”

struct S{
void f1() {}//the old rule
void f2() && {}
void f3() & {}
};
int main()
{
S().f1();//Here, I THINK const this is bound to non-const, thus allowing calling non-const member functions.(the comment here is not quoted from the book.)
S().f2();
S().f3();//not okay
return 1;
}

Use -std=c++11 compilation option.
So, this means C++ designers have realized that the old rule of allowing temporary objects, which have implicit const this*, to call non-const member function is not so good. So in C++11 they introduced & and && suffixing function declaration.

But when it comes to the design philosophy of the old rule of allowing temporary objects calling non-const member function, I think it's not worth diving into this. C++ 11 has make an effort to "emend" this.(Or let programmer control this.)

Dangling reference when returning reference to reference parameter bound to temporary

I like to keep an example class A around for situations like this. The full definition of A is a little too lengthy to list here, but it is included in its entirety at this link.

In a nutshell, A keeps a state and a status, and the status can be one of these enums:

    destructed             = -4,
self_move_assigned = -3,
move_assigned_from = -2,
move_constructed_from = -1,
constructed_specified = 0

That is, the special members set the status accordingly. For example ~A() looks like this:

~A()
{
assert(is_valid());
--count;
state_ = randomize();
status_ = destructed;
}

And there's a streaming operator that prints this class out.

Language lawyer disclaimer: Printing out a destructed A is undefined behavior, and anything could happen. That being said, when experiments are compiled with optimizations turned off, you typically get the expected result.

For me, using clang at -O0, this:

#include "A.h"
#include <iostream>

int
main()
{
A a{1};
A b{2};
A c{3};
A&& x = a + b + c;
std::cout << x << '\n';
}

Outputs:

destructed: -1002199219

Changing the line to:

    A x = a + b + c;

Results in:

6


Related Topics



Leave a reply



Submit