10

I am learning C++ from a background of mostly C and Java and I am curious on what is the best way to return an object in C++ without having to copy the object. From my understanding, C++11 introduced rvalue references (&&) to move data from a temporary variable (as oppose to copying). Example:

std::string getStr(){
   return "Hello";
}

std::string &&hello = getStr();

Another way I could think of is using a shared pointer.

std::tr1::shared_ptr<std::string> getStr(){

    std::tr1::shared_ptr<std::string> hello(new std::string("Hello"));
    return hello;

}

auto hello = getStr();

I am thinking maybe an rvalue reference is better but I'd like a second opinion first before I use it. Which is better?

Also is it recommended that fields in a class be rvalue references if they won't be set using the constructor? Example:

class StringHolder{

   public:
     std::string &&str;
};

StringHolder sh;
sh.str = getStr();

Thank you guys very much!

DanB91
  • 1,399
  • 1
  • 13
  • 23
  • 1
    You might find [A Brief Introduction to Rvalue References](http://www.artima.com/cppsource/rvalue.html) useful – Frank Boyne Jul 08 '12 at 00:53
  • Modern implementations of the standard library will use the small string optimization, so dynamically allocating a string "Hello" could be slower than just returning one by value. – bames53 Jul 08 '12 at 01:38

5 Answers5

11

This question will probably be closed as a duplicate. It is an oft-asked question. However I would like to answer it anyway.

In C++11, when you are the client of an object like std::string, you should pass it around by value and not worry so much about efficiency:

std::string getStr(){
   return "Hello";
}

std::string hello = getStr();

At this level you do not need to be concerned with rvalue references. Just know that std::string can "copy" from rvalues (such as the return from getStr()) very efficiently. This "copy" is actually called a "move" and is enabled automatically because you are copying from an rvalue (an anonymous temporary).

Don't try to optimize copying by reverting to reference counting. Only use reference counting if you need shared ownership semantics. This statement isn't always true. But for learning the basics, it is close enough that it is a good rule of thumb to follow.

You need to start worrying about rvalue references when you design your class that needs to be passed around by value:

class Widget
{
    // pointer to heap data
public:
    // ...
};

For a brief introduction to rvalue references and move semantics, N2027 is a decent tutorial. N2027 is too long to be pasted in here, but short enough to be an easy read. std::string follows the basics laid down in N2027 to enable you to pass it around by value guilt free. And you can follow those same design patterns in your Widget.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Thanks very much for this! I am slowly understanding the tutorial and the concept of move constructors. Do most objects in std have the move constructor such as a vector (that is, is it smart to return a large vector by value)? Also, in your example Widget class, is that "pointer to heap data" comment just specific to this example or is it more common to use pointers as fields as opposed to value types (though, I would infer use of a pointer or value field depends on the situation)? – DanB91 Jul 08 '12 at 04:15
  • Also, I know this depends on the move constructor of the object, but I am assuming, as a general rule, if you have obj o = std::move(p);, p should probably never be used again, correct? – DanB91 Jul 08 '12 at 04:44
  • @DanB91 : Not until it's assigned to, anyway. – ildjarn Jul 08 '12 at 07:27
  • @DanB91: All objects in the std::lib that can take advantage of move, have done so. This includes vector and string, but not complex. For complex there is no resource to transfer (it is just a pair of floating point values), and so a copy and a move would be the exact same operation. Widget might contain pointers, smart pointers, strings, vectors, whatever. If it owns any heap memory, either directly with a pointer, or indirectly with a vector (just as an example), then Widget can have a move constructor which is faster than its copy constructor. – Howard Hinnant Jul 08 '12 at 14:02
  • @DanB91: What can I do with a moved-from object? http://stackoverflow.com/a/7028318/576911 – Howard Hinnant Jul 08 '12 at 14:04
2

Often you can avoid copies by returning a const reference to a member variable. For example:

class Circle {
    Point center;
    float radius;
public:
    const Point & getCenter() const { return center; };
};

This is equivalent to return &center in execution and in fact will likely be inlined simply resulting in direct (read-only) access of the member from the caller.

In cases where you construct a temporary to return the compiler may elide the extra copy as an optimization, requiring no special syntax on your part.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
2

By default you should simply pass things by value. If a function does not need to modify or copy a parameter then it can be taken by const&, but otherwise just take parameters by value. Return objects by value and leave it to move semantics or RVO to avoid a copy.


C++ uses and encourages passing objects 'by-value'. Move semantics are a feature that allows code to do this while making fewer copies than before they were introduced. Typically code does not need to use rvalue reference types directly in order to gain the benefit of move semantics. This is because temporary objects are rvalues and overload resolution will automatically select methods that take rvalue references.

For example:

std::string getStr(){
   return "Hello";
}

std::string hello = getStr();

This code uses move semantics. return "Hello" constructs a string temporary and so the constructor used to initialize the return value object will be the move constructor (though actually the return value optimization will almost certainly avoid this move). Then the value returned by getStr() is a temporary, so the constructor selected for string hello is again the move constructor.

I think you can also do std::string &&hello = getStr();, but it's probably not necessary since hello will already be move constructed.

Another way I could think of is using a shared pointer

I don't think smart pointers are a generally a good idea unless you really need the specific ownership semantics they provide. Using straightforward 'by-value' passing is a good default and move semantics generally allow you to use it without extra copying.

Also is it recommended that fields in a class be rvalue references if they won't be set using the constructor?

No, member references are not usually a good idea. They make things more complicated and you aren't even allowed to use them as your example shows. If your example code were allowed then sh.str = getStr(); would be undefined behavior, because you're trying to assign to a non-existant object. It's like std::string *str; *str = getStr();.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • From what I gathered I thought that rvalue references save returned values from being destroyed. In fact, I wrote a quick "std::string &&hello = getStr();" program and it worked fine (as opposed to doing string *hello; *hello = getStr();, which obviously caused a seg fault). But now I am slowly understanding the use of && and will take the advice of returning objects by value (as long as they are either small or implement the move constructor). Thanks! – DanB91 Jul 08 '12 at 04:25
  • @DanB91 Actually I think you're right that you can capture a return value with an rvalue reference. – bames53 Jul 08 '12 at 07:06
1

Rvalue refernces will be much faster, as they are an intrinsic language feature, rather than a reference counting class. Using a smart or shared pointer here will give no gain, and greatly decrease clarity. However, the rvalue references are a part of the language designed for this kind of thing. Use the rvalue reference.

Linuxios
  • 34,849
  • 13
  • 91
  • 116
0

If you want to use smart pointer in this case, maybe unique_ptr is a better choise.

Gang YIN
  • 2,509
  • 2
  • 21
  • 25