160

for example:

Beta_ab&& Beta::toAB() const {
    return move(Beta_ab(1, 1));
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Neil G
  • 32,138
  • 39
  • 156
  • 257

2 Answers2

307
Beta_ab&&
Beta::toAB() const {
    return move(Beta_ab(1, 1));
}

This returns a dangling reference, just like with the lvalue reference case. After the function returns, the temporary object will get destructed. You should return Beta_ab by value, like the following

Beta_ab
Beta::toAB() const {
    return Beta_ab(1, 1);
}

Now, it's properly moving a temporary Beta_ab object into the return value of the function. If the compiler can, it will avoid the move altogether, by using RVO (return value optimization). Now, you can do the following

Beta_ab ab = others.toAB();

And it will move construct the temporary into ab, or do RVO to omit doing a move or copy altogether. I recommend you to read BoostCon09 Rvalue References 101 which explains the matter, and how (N)RVO happens to interact with this.


Your case of returning an rvalue reference would be a good idea in other occasions. Imagine you have a getAB() function which you often invoke on a temporary. It's not optimal to make it return a const lvalue reference for rvalue temporaries. You may implement it like this

struct Beta {
  Beta_ab ab;
  Beta_ab const& getAB() const& { return ab; }
  Beta_ab && getAB() && { return move(ab); }
};

Note that move in this case is not optional, because ab is neither a local automatic nor a temporary rvalue. Now, the ref-qualifier && says that the second function is invoked on rvalue temporaries, making the following move, instead of copy

Beta_ab ab = Beta().getAB();
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 71
    I had always assumed the dangling reference problem went away automagically when the return type was an r-value reference. Glad I got that straighted out before it bit me. Stack smashing bugs suck. – deft_code Jul 15 '09 at 03:03
  • 33
    :) Really, rvalue references are "just references" like lvalue references are. they don't copy or store anything. – Johannes Schaub - litb Mar 10 '10 at 02:17
  • And even if it doesn't elide the constructors, the compiler something knows it can return an rvalue-ref for you automatically safely. For example, according to my experiments, `return x;` is the same as `return std::move(x)`. (where `x` is a local variable (i.e. this point I've made doesn't directly apply to the original question about returning a temporary)). – Aaron McDaid May 10 '13 at 18:47
  • What's with two ampersands in each signature? – Arlen Dec 31 '13 at 03:42
  • 11
    what does the const & qualifier on a member function more than a simple const ? – galinette Apr 11 '14 at 11:33
  • 1
    @Arlen: [rvalue references](http://en.wikipedia.org/wiki/C++11#Rvalue_references_and_move_constructors), added in C++11. – DevSolar Oct 07 '14 at 11:00
  • 4
    +1-ed, but broken link: [BoostCon09 Rvalue References 101](https://www.boostpro.com/trac/wiki/BoostCon09/RValue101) – Siu Ching Pong -Asuka Kenji- Mar 30 '15 at 00:45
  • 3
    @galinette These are [ref-qualifiers](http://en.cppreference.com/w/cpp/language/member_functions). – Malcolm Oct 20 '15 at 10:55
  • 3
    @JohannesSchaub-litb I tried "Beta_ab && getAB() ..." and "Beta_ab getAB() ..." (with and without the && in the return type). They produced the same result. Is this && really necessary? – Candy Chiu Mar 21 '16 at 14:39
  • 2
    @NeilG There're two sets of &&, one next to the return type, one at the end of the declaration. The second set means the function should be invoked on a rvalue. I am asking about the first set. Did I miss something? – Candy Chiu Mar 21 '16 at 16:35
  • @CandyChiu I think what Johannes is saying in his answer is that the first set returns a dangling reference. The fact that "it produced the same result" was just an accident. – Neil G Mar 21 '16 at 16:43
  • 1
    @NeilG In the second set of code (the correct set without dangling reference), is there a diff between "Beta_ab && getAB() &&" vs " Beta_ab getAB() &&". What I tried with gnu and VS indicates that the first set of && doesn't change the behavior. I want to know in what circumstances does this declaration alters behavior. Thanks. – Candy Chiu Mar 21 '16 at 17:04
  • @candychiu the second one returns by value, which might make a copy. Did you declare a copy constructor and print something when it is called? – Neil G Mar 21 '16 at 17:14
  • @NeilG I removed the copy constructor, and it still works. I printed the content, it was moved. Try this: https://ideone.com/wpXm73. foo1 and foo2 makes no difference. – Candy Chiu Mar 21 '16 at 17:23
  • @candychiu all classes have a default copy constructor. Did you actually delete it using the new syntax? Please ask another question if you're still confused. I think there are a lot of misunderstandings in your questions – Neil G Mar 21 '16 at 17:25
  • @NeilG Yes. If you open up the code in the link, the copy constructor and operator= has been explicitly deleted. Do you mind looking at the link? If you can't come up with answer after looking, I can open another question. – Candy Chiu Mar 21 '16 at 17:30
  • 2
    @CandyChiu I recommend that you create a new SO question and pose your question there, and asking about something like "diff between && and no ref for &&-qualified functions". – Johannes Schaub - litb Mar 21 '16 at 17:43
  • 1
    what if I do this: `Beta_ab && Beta::toAB() const { return Beta_ab(1, 1); }` Is this correct? – Yves May 26 '16 at 09:04
  • @JohannesSchaub-litb I think I understand this, but why can't I prove it dangles? : `int&& return_1() { int a{ 1 }; return move(a); // dangling reference? }` is working without problem. Do you have an example where it fails? – Pedro Reis Mar 08 '17 at 13:22
  • Wait, isn't `std::move` just a `static_cast` to rvalue-reference? So if we removed the `move` from Johannes' example and just said `return ab;` the function would return an rvalue-reference to `Beta_ab` initialized with `ab`, that is, exactly the result of `move(ab)` no? But in this case, the `move` would be superfluous, contradicting Johannes' statement that "the move is not optional". – igel Dec 04 '21 at 16:02
  • Is this right? An rvalue reference extends the lifetime of the temporary object to which it refers, and so that temporary value doesn't become dangling immediately. Assuming the user assigns the result of toAb(), then the lifetime is extended until the move-assignment operator completes. Of course, if the user binds the result to a reference, then here be dragons, and I certainly agree that it's better not to return rvalue references since RVO removes the need in a much safer way, but I challenge the dangling-reference argument. Note that std::get() returns an rvalue reference when given one. – sircolinton Apr 03 '22 at 01:51
  • As a continuation to my comment, I found the following answers interesting: https://stackoverflow.com/a/31360610/196429, https://stackoverflow.com/a/6030496/196429 (and also the comments by Howard and Aaran) – sircolinton Apr 03 '22 at 01:56
-2

It can be more efficient, for example, in a bit different context:

template <typename T>
T&& min_(T&& a, T &&b) {
    return std::move(a < b? a: b);
}

int main() {
   const std::string s = min_(std::string("A"), std::string("B"));
   fprintf(stderr, "min: %s\n", s.c_str());
   return 0;
}

As an interesting observation, on my machine clang++ -O3 generates 54 instructions for code above versus 62 instructions for regular std::min. However, with -O0 it generates 518 instructions for code above versus 481 for regular std::min.

wonder.mice
  • 7,227
  • 3
  • 36
  • 39
  • I'm confused by your answer. Had tried a similar (maybe) version but failed: https://ideone.com/4GyUbZ Could you explain why? – Deqing Apr 11 '18 at 00:25
  • You used reference on temporary object in `for(:)` and integrating over deallocated object. Fix: https://ideone.com/tQVOal – wonder.mice Apr 11 '18 at 17:42
  • 20
    isn't this answer actually wrong? for template parameter T, T&& is *not* an r-value reference, but rather a universal reference, and for that case, we should always call std::forward, _not_ std::move! Not to mention, that this answer directly contradicts the top-voted one above. – xdavidliu Nov 03 '19 at 17:49
  • 2
    @xdavidliu this answer is a contrived example how returning by rvalue can be more efficient. `std::move()` is just used as an explicit cast to illustrate the point more clearly. It's not a code you would copy-paste into your project. It doesn't contradict top-voted answer, because there temporary object is created inside the function. Here returned object is one of the arguments (temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created). – wonder.mice Apr 15 '20 at 19:08
  • 9
    @wonder.mice then please replace T with std::string; there's no need to use templates at all here, and using T&& as a r-value reference is just awful style that unnecessarily confuses people new to templates and r-values. – xdavidliu Apr 15 '20 at 19:14