Question

I have a factory function which return a stl container:

const std::vector<int> f(...) {
    std::vector<int> retval;
    return retval;
}

I guess it is ok to define a stl instance as follow(no error):

const std::vector<int> stl_instance(f(...));

But is it efficient to do so?

Is the temporary stl object directly assigned to stl_instance?

Was it helpful?

Solution

Returning a const rvalue is an anti-pattern in C++11. First consider returning non-const rvalues:

std::vector<int> f(int n) 
{ 
    return std::vector<int>(n); 
}

int main() 
{ 
    std::vector<int> v; 
    v = f(3); 
} 

In C++98/03, this code will go to the heap at least twice:

  1. To create the vector inside of f (if the RVO applies)
  2. To assign from f's return to v.

If your compiler does not apply the RVO, you get 3 heap allocations.

In C++11, you only get 1 heap allocation: to create the vector inside of f. This happens regardless of the RVO. The reason is that all the STL containers have move constructors and move assignment operators that have the signatures

vector( vector&& other );
vector& operator=( vector&& other );

The rvalue references && will move the resources from inside your creation function directly to their destination. However, your code has the signature

const std::vector<int> f(int n) 
{ 
    return std::vector<int>(n); 
}

will disable the move semantics because a T&& (i.e. the argument of the move constructor and assignment operator) will not bind to a const rvalue parameter (i.e. the return value of your function). This effectively makes your code run as under C++98/03 (i.e. with 2 or 3 heap allocations).

OTHER TIPS

It is fine and legal code.
The compiler will take care of necessary optimizations(if it can) through copy elision.

The temporary f(...) is guaranteed to live at least until the expression ends. Note that the expressions ends just after the return from stl_instance(f(...)), to be precise ;(the semicolon at the end of call). So it is perfectly valid.

C++03 Standard §12.2/3:

Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top