Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In c++11, is there ever still a need to pass in a reference to an object that will accept the output of a function?

Prior to C++11, if I had a function that operated on large objects, my instinct would be to write functions with this kind of prototype.

void f(A &return_value, A const &parameter_value);

(Here, return_value is just a blank object which will receive the output of the function. A is just some class which is large and expensive to copy.)

In C++11, taking advantage of move semantics, the default recommendation (as I understand it) is the more straightforward:

A f(A const &parameter_value);

Is there ever still a need to do it the old way, passing in an object to hold the return value?

like image 221
Rob Lachlan Avatar asked Jun 16 '12 04:06

Rob Lachlan


2 Answers

Others have covered the case where A might not have a cheap move constructor. I'm assuming your A does. But there is still one more situation where you might want to pass in an "out" parameter:

If A is some type like vector or string and it is known that the "out" parameter already has resources (such as memory) that can be reused within f, then it makes sense to reuse that resource if you can. For example consider:

void get_info(std::string&);
bool process_info(const std::string&);

void
foo()
{
    std::string info;
    for (bool not_done = true; not_done;)
    {
        info.clear();
        get_info(info);
        not_done = process_info(info);
    }
}

vs:

std::string get_info();
bool process_info(const std::string&);

void
foo()
{
    for (bool not_done = true; not_done;)
    {
        std::string info = get_info();
        not_done = process_info(info);
    }
}

In the first case, capacity will build up in the string as the loop executes, and that capacity is then potentially reused on each iteration of the loop. In the second case a new string is allocated on every iteration (neglecting the small string optimization buffer).

Now this isn't to say that you should never return std::string by value. Just that you should be aware of this issue and apply engineering judgment on a case by case basis.

like image 166
Howard Hinnant Avatar answered Oct 09 '22 23:10

Howard Hinnant


It is possible for an object to be large and expensive to copy, and for which move semantics cannot improve on copying. Consider:

struct A {
    std::array<double,100000> m_data;
};

It may not be a good idea to design your objects this way, but if you have an object of this type for some reason and you want to write a function to fill the data in then you might do it using an out param.

like image 32
bames53 Avatar answered Oct 09 '22 23:10

bames53