24

I'm aware that it's normally not a good idea to return with std::move, i.e.

bigObject foo() { bigObject result; /*...*/ return std::move(result); }

instead of simply

bigObject foo() { bigObject result; /*...*/ return result; }

because it gets in the way of return value optimization. But what in the case of a function with multiple different returns, particularly something like

class bar {
  bigObject fixed_ret;
  bool use_fixed_ret;
  void prepare_object(bigObject&);
 public:
  bigObject foo() {
    if(use_fixed_ret)
      return fixed_ret;
     else{
      bigObject result;
      prepare_object(result);
      return result;
    }
  }
};

I think normal return value optimization is impossible in such a function, so would it be a good idea to put in

      return std::move(result);

here, or should I rather do (IMO uglier, but that's debatable)

  bigObject foo() {
    bigObject result;
    if(use_fixed_ret)
      result = fixed_ret;
     else{
      prepare_object(result);
    }
    return result;
  }
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • I can't reference the standard so I wont make an answers, but I am sure that you don't need std::move, I think you are confusing RVO and copy elision, copy elision is optimisation that benifits on some compilers having a single return path. BigObject will become an R-value regard of where it returned. – 111111 Mar 02 '12 at 11:38
  • Yes, I think copy elision is what I mean in the beginning. – leftaroundabout Mar 02 '12 at 11:41
  • 1
    Also if you like clean code and code level optimisations are you seem to, :), the you can do rid of the `else{...` branch in your `foo()` function. As if the first statement is true then the second wont be evaluated. – 111111 Mar 02 '12 at 11:45
  • In your example, you are moving your member variable `fixed_ret`. So calling the function multiple times might not work. – balki Mar 02 '12 at 16:13
  • @balki "moving your member variable `fixed_ret`" am I? How is that? – leftaroundabout Mar 02 '12 at 18:04
  • @leftaroundabout, @balki, `return fixed_ret;` will make a copy. – scpayson Oct 09 '14 at 06:48

1 Answers1

34

For local variables, there's no need to std::move them in the return statement most of the time, since the language actually demands that this happens automatically:

§12.8 [class.copy] p32

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ]


† Copy elision is very restricted in where it can be applied (§12.8/31). One such restriction is that the type of the source object has to be the same as the cv-unqualified return type of the function when dealing with a return-statement. It's also not applicable for subobjects of local variables that are about to go out of scope.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 2
    And here is your standards reference. – 111111 Mar 02 '12 at 11:42
  • 1
    Allright... I always have my difficulties with the formulations in the standard, read it five times now and still don't quite understand what it's supposed to mean. But you say, in my example, it simply means "the first version is as good as it gets"? Then I'll trust you there. – leftaroundabout Mar 02 '12 at 11:59
  • 2
    @leftaroundabout: It basically says "If the object is local, and is returned by value, first try to return it as an rvalue (as `std::move` would do). If that doesn't find a move ctor, try the same but this time as an lvalue. – Xeo Mar 02 '12 at 12:11
  • 1
    @Xeo Can I simplify that as first try the move constructor if not, use the copy constructor? – balki Mar 02 '12 at 16:16