2

Please note that there are previous answers concerning template functions or member functions but this question is only about non-template non-member functions. std::move() returns T&&, for example but it is a template function.

Is there ever a good reason to use T&& as a return type from a non-template and non-member function, where T is any arbitrary type?

For example when would you ever use the following?

T&& fn()
{
....
return ....
}

I have seen examples where this is used but in all the examples, the developer misunderstood move semantics and should have returned by value and taken advantage of NRVO.

T33C
  • 4,341
  • 2
  • 20
  • 42
  • @interjay, thanks for moderating the site. The other question that you say is a duplicate regards template functions whereas my question is about non-template functions. – T33C Jan 16 '16 at 22:00
  • 3
    That question is not about template functions - it doesn't even mention the word "template". The accepted answer does have a class template (not a function template) in the example, but it would be the same if it wasn't a template. – interjay Jan 16 '16 at 22:03
  • 2
    More detailed [exlanation](http://stackoverflow.com/a/1116763/4074081) – dewaffled Jan 16 '16 at 22:06
  • @frymode, thanks for the link. I would expect Beta_ab getAB() && { return move(ab); } to work just as well as Beta_ab && getAB() && { return move(ab); } because of RVO. I suspect that there is no case where you need to have a T&& return type but there are cases where it is optional. If this is true then it would be safer to ban T&& return types (using lint tools) to simplify the language and reduce errors. – T33C Jan 16 '16 at 22:15
  • 2
    RVO cannot be applied there since return value is not a temporary on stack but a member variable. When explicitly moving it we state to the compiler that it will not be used anywhere else anyway and may be copied more effectively via move. – dewaffled Jan 16 '16 at 22:19
  • @frymode, +2 thanks for your help. That makes sense but sounds a little dangerous. Probably ok if method is && ref qualified. – T33C Jan 16 '16 at 23:30
  • @interjay, what is the etiquette? Should I delete this question or leave it? – T33C Jan 16 '16 at 23:31
  • 2
    It's fine to leave duplicate questions. It might make it easier to google this issue in the future. – interjay Jan 16 '16 at 23:56

2 Answers2

0

Suppose we were to have a particle system. We want to implement this as a pool of particles. This means that we are going to want to recycle the same particles over and over again, so to reuse resources, we will want to pass around rvalues.

Now, suppose that our particles have a very short life time, but we want something to happen when they "expire" (like incrementing an integer x) but we still want to recycle them. Now, suppose that we want to be able to specify x. But, now what do we do?

On move, we want to be able to call a function to increment a variable, but that variable must fluctuate. We wouldn't want to put this into a destructor because this would involve either templates to derive the exact function call at compile time or we would need a std::function or function pointer to refer to the function from inside a particle, wasting space. What we want to do is to be able to take in an expiring value, do an action, and then forward it. In other words, we want an outgoing-move with a side effect specifically on the conversion from lvalue to rvalue.

It does matter on which action you do it--on lvalue to rvalue conversion or on receiving an into another Particle with operator= or operator(). Either you do it when an object receives a value or when you take the rvalue in. But suppose we want to do this for many different types of objects--do you want to write 5 or more different move functions for each of 5 different classes, or perhaps should we parameterize over the types with a external templated function?

How else would you do this? You're still going to use std::move() from inside the object, but we want to couple it with a function external to the object, because this would represent a side effect with a move.

Coliru: http://coliru.stacked-crooked.com/a/0ff11890c16f1621

#include <iostream>
#include <string>

struct Particle {
    int i;    
};

template <typename T>
T&& move( T& obj,  int (*fn)(int) , int& i ) {
    i = fn(i);
    return(std::move(obj));
}

int increment(int i) { return i+1; }

int main() {
    // Have some object pool we want to optimize by moving instead of creating new ones.
    // We'll use rvalue semantics so we can "inherit" lifetime instead of copying.
    Particle pool[2000];

    // Fill up the "particles".
    for (auto i = 0; i < 2000; ++i) {
        pool[i].i = i;    
    }

    // Perform the moves with side effects.
    int j = 0;
    for (auto i = 0; i < 1999; ++i) {
        pool[i+1] = move<Particle>(pool[i], &increment, j);
        std::cout << "Moves performed: " << j << '\n';
    }

}
CinchBlue
  • 6,046
  • 1
  • 27
  • 58
  • Interesting use case. I am looking for an example of where to use this in non-template functions (and not member functions.) – T33C Jan 17 '16 at 11:38
  • @T33C Although the function I gave is a template function, it is not to forward the type or merely coerce it to another type. It's for regular type generics. – CinchBlue Jan 18 '16 at 02:03
-1

Sometimes, it would be handy to have a method that returns *this as an l-value reference, so that you can pass a temporary to a function that accepts an l-value reference.

func(foo_t().lval_ref());

So, why would people want to do this? Suppose a function takes an l-value reference as an output variable. If the caller does not actually need this output, it would be convenient to pass a temporary instead of defining some dummy variable unused, and then static_cast<void>(unused); to suppress any warnings of unused variable.

Similarly, one may want to have a method that returns *this as an r-value reference, which is used the same way as std::move(). Doing this allows you greater flexibility. You can do any tricky things you want within the method implementation :-)

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • "Pass a temporary to a function that accepts an lvalue reference." I was trying to figure out why somebody might've downvoted this, but I don't think there's a case where you want to treat a mutable rvalue as a mutable lvalue because, by nature, rvalues are not reliable--they're meant to expire or have ownership taken care of and should not be assigned to. `T&&` is already castable to `const T&` so that case is accounted for. – CinchBlue Jan 17 '16 at 05:43
  • @VermillionAzure Suppose the function takes an l-value reference as an output variable. If the caller does not actually need this output, it would be convenient to pass a temporary instead of defining some variable `var`, and then `static_cast(var);` – Lingxi Jan 17 '16 at 06:25
  • How would that be helpful? If you don't need the output, then just don't feed it into anything. No need for any casting--you can use functions without having to use their output. Additionally, `*this` is an lvalue reference already, but it's an rvalue of a lvalue reference. – CinchBlue Jan 17 '16 at 06:49
  • @VermillionAzure The function needs an l-value ref for output. But, you don't actually need this output. In order to call this function, you still need to supply an l-value ref anyway. If not to use a temporary, you have to define a dummy variable and pass it to the function. In order to suppress any warning of unused variable, you do the cast `static_cast(unused);`. – Lingxi Jan 17 '16 at 06:56