3

I understand that c++ only allows rvalues or temp objects to bind to const-references. (Or something close to that...)

For example, assuming I have the functions doStuff(SomeValue & input)
and SomeValue getNiceValue() defined:

/* These do not work */
app->doStuff(SomeValue("value1"));
app->doStuff(getNiceValue());

/* These all work, but seem awkward enough that they must be wrong. :) */

app->doStuff(*(new SomeValue("value2")));

SomeValue tmp = SomeValue("value3");
app->doStuff(tmp);

SomeValue tmp2 = getNiceValue();
app->doStuff(tmp2);

So, three questions:

  1. Since I am not free to change the signatures of doStuff() or getNiceValue(), does this mean I must always use some sort of "name" (even if superfluous) for anything I want to pass to doStuff?

  2. Hypothetically, if I could change the function signatures, is there a common pattern for this sort of thing?

  3. Does the new C++11 standard change the things at all? Is there a better way with C++11?

Thank you

Community
  • 1
  • 1
nonot1
  • 2,788
  • 4
  • 25
  • 41

5 Answers5

6

An obvious question in this case is why your doStuff declares its parameter as a non-const reference. If it really attempts to modify the referred object, then changing function signature to a const reference is not an option (at least not by itself).

Anyway, "rvalue-ness" is a property of an expression that generated the temporary, not a property of temporary object itself. The temporary object itself can easily be an lvalue, yet you see it as an rvalue, since the expression that produced it was an rvalue expression.

You can work around it by introducing a "rvalue-to-lvalue converter" method into your class. Like, for example

class SomeValue {
public:
  SomeValue &get_lvalue() { return *this; }
  ...
};

and now you can bind non-const references to temporaries as

app->doStuff(SomeValue("value1").get_lvalue());
app->doStuff(getNiceValue().get_lvalue());

Admittedly, it doesn't look very elegant, but it might be seen as a good thing, since it prevents you from doing something like that inadvertently. Of course, it is your responsibility to remember that the lifetime of the temporary extends to the end of the full expression and no further.

Alternatively, a class can overload the unary & operator (with natural semantics)

class SomeValue {
public:
  SomeValue *operator &() { return this; }
  ...
};

which then can be used for the same purpose

app->doStuff(*&SomeValue("value1"));
app->doStuff(*&getNiceValue());

although overriding the & operator for the sole purpose of this workaround is not a good idea. It will also allow one to create pointers to temporaries.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    +1. Overriding `operator T&()` (assuming your template parameter is `T`) is more convenient, and seems reasonable in this particular situation. – Tony Delroy Aug 14 '12 at 00:51
  • 2
    You can also make this a free function `template T& as_lvalue(T&& v){ return v; }`. – Xeo Aug 14 '12 at 07:29
4
  1. Since I am not free to change the signatures of doStuff() or getNiceValue(), does this mean I must always use some sort of "name" (even if superfluous) for anything I want to pass to doStuff?

Pretty much yes. This signature assumes that you want to use input as an "out" parameter. So the author of doStuff believes that if the client passes an anonymous object in, that is a logical error best caught at compile time.

  1. Hypothetically, if I could change the function signatures, is there a common pattern for this sort of thing?

In C++11 only, you could change or overload like so:

doStuff(SomeValue&& input);

Now input will only bind to an rvalue. If you've overloaded, then the original will get the lvalues and your new overload will get the rvalues.

  1. Does the new C++11 standard change the things at all? Is there a better way with C++11?

Yes, see the rvalue reference overload above.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
4

std::forward is usually the way to 'convert' value category. However it is prohibited to accept rvalues when forwarding as an lvalue, for the same reasons that a reference to non-const won't bind to an rvalue. That being said, and assuming you don't want to overload doStuff (otherwise see Hinnant's answer), you can write a utility yourself:

template<typename T>
T& unsafe_lvalue(T&& ref)
{ return ref; }

And use it like so: app->doStuff(unsafe_lvalue(getNiceValue())). No intrusive modification needed.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
1

You must always use a name for values you pass to doStuff. The reasons for this are covered in detail at How come a non-const reference cannot bind to a temporary object?. The short summary is that passing a reference implies that doStuff can change the value that it references, and that changing the value of a reference to a temporary is something that the compiler should not let you do.

I'd avoid the first solution, because it allocates memory on the heap that is never freed.

The common pattern for solving this is to change doStuff's signature to take a const reference.

Community
  • 1
  • 1
razeh
  • 2,725
  • 1
  • 20
  • 27
  • 3
    That's a pretty flawed and misleading logic (even tough it is quoted quite often). Calling non-const methods on some object is not necessarily done to alter some state of the object to be inspected later. Such calls can simply be made for their useful *side effects*. Yes, the object will die afterwards, but I don't really care, if it did what it had to do as long as it was alive. – AnT stands with Russia Aug 14 '12 at 00:15
  • I disagree; if the calls are only being used for the side effects, why can't they be const? – razeh Aug 14 '12 at 00:24
  • 1
    I don't see the connection. The methods might as well *modify* the internal state of the object. But it is a purely temporary state, which I don't care to inspect later. For example, writing data to some sort of output stream might change internal state of the stream and have useful side-effects. Yet I don't care to inspect the state of the stream afterwards (which is why the temporary is OK). And I don't see why stream output methods should be `const` in that case. – AnT stands with Russia Aug 14 '12 at 00:28
  • For another example, let's say I want to call `std::for_each` with some *stateful* functor, which modifies itself each time it is invoked. It is normal in that case to have non-const `operator ()` in that functor. This will work because "by defualt" `for_each` accepts the functor by value. But what if I don't want to pass it by value? I want to pass it by reference (non-const of course). And I still want to use a temporary as an argument of `for_each` (since I don't care about the final state of the functor). I cannot do that without using workarounds like the ones in my answer. – AnT stands with Russia Aug 14 '12 at 00:36
  • I think we are arguing about two different things. I'm pointing out that the language only allows rvalues or temp objects to bind to const-references (1.3.3.1.4.3 of the standard), and that this was done to prevent modifying temporaries. Are you arguing that this isn't in the standard, that it was put in the standard for a different reason, or that the reason was bad? – razeh Aug 14 '12 at 00:54
  • 1
    Well, it is quite possible that the rationale behind this restriction was to prevent modifications to temporary objects. All I am saying that this rationale is not bulletproof. Modifications to temporary objects can be dangerous at time, but they are not useless. – AnT stands with Russia Aug 14 '12 at 01:04
  • Ah, now I get it. Edited to take that into account. – razeh Aug 14 '12 at 01:37
0

Unfortunately, I think the answer is that you have to have a named object to pass into doStuff. I don't think there is a C++11 feature that gives you flexibility in this area, nor have I heard of any design pattern for this type of situation.

If this is something you expect to encounter often in your program, I would write an interface more suited to the needs of the current app. If it's just a one off, I would tend to just create a temp object to store the result (as you have done).

Rollie
  • 4,391
  • 3
  • 33
  • 55