Will RVO happen when returning std::pair?
Asked Answered
P

4

9

A function needs to return two values to the caller. What is the best way to implement?

Option 1:

pair<U,V> myfunc()
{
...
return make_pair(getU(),getV());
}

pair<U,V> mypair = myfunc();

Option 1.1:

// Same defn
U u; V v;
tie(u,v) = myfunc();

Option 2:

void myfunc(U& u , V& v)
{
u = getU(); v= getV();
}

U u; V v;
myfunc(u,v);

I know with Option2, there are no copies/moves but it looks ugly. Will there be any copies/moves occur in Option1, 1.1? Lets assume U and V are huge objects supporting both copy/move operations.

Q: Is it theoretically possible for any RVO/NRVO optimizations as per the standard? If yes, has gcc or any other compiler implemented yet?

Puissance answered 26/12, 2012 at 16:55 Comment(4)
I don't know of anything about std::pair that would inhibit RVO/NRVO. It's generally fairly easy to test by including a copy constructor that tells you when a copy happens.Antiworld
g++ implements RVO that prevents copying the pair, however you still have the copy of u and v into the pair.Kuopio
I ran some tests, and I found that with g++, which one was faster depended a lot on what inlining was possible and the complexity of the copy constructors for U and V. If you are just looking for performance, I think you'll have to profile it to determine which is fastest.Kuopio
This is not really an answer to your question but I would suggest to use simple struct instead of pair. Pair is ugly and heavy weight. Once I've seen a code like time.first; time.second; Unreadable. time.minutes; time.seconds; is much better.Unpeopled
M
8

Will RVO happen when returning std::pair?

Yes it can.

Is it guaranteed to happen?

No it is not.


C++11 standard: Section 12.8/31:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects.

Copy elision is not a guaranteed feature. It is an optimization compilers are allowed to perform whenever they can. There is nothing special w.r.t std::pair. If a compiler is good enough to detect an optimization opportunity it will do so. So your question is compiler specific but yes same rule applies to std::pair as to any other class.

Monda answered 26/12, 2012 at 17:6 Comment(0)
H
4

While RVO is not guaranteed, in C++11 the function as you have defined it I believe MUST move-return at the very least, so I would suggest leaving the clearer definition rather than warping it to accept output-variables (Unless you have a specific policy for using them).

Also, even if this example did use RVO, your explicit use of make_pair means you will always have at least one extra pair construction and thus a move operation. Change it to return a brace-initialized expression:

return { getU(), getV() };
Hildebrand answered 27/12, 2012 at 3:7 Comment(0)
C
2

If you need to do additional work on u and v after having created the pair, I find the following pattern pretty flexible in C++17:

pair<U,V> myfunc()
{
  auto out = make_pair(getU(),getV());
  auto& [u, v] = out;
  // Work with u and v
  return out;
}

This should be a pretty easy case for the compiler to use named return value optimization

Chemisorb answered 20/8, 2020 at 8:1 Comment(0)
B
1

RVO or Copy elision is dependant on compiler so if you want to have RVO and avoid call to Copy constructor best option is to use pointers.

In our product we use use pointers and boost containers pointer to avoid Copy constructor. and this indeed gives performance boost of around 10%.

Coming to your question, In option 1 U and V's copy constructor will not be called as you are not returning U or V but returning std::pair object so it's copy constructor will be called and most compilers will definately use RVO here to avoid that.

Thanks Niraj Rathi

Bursary answered 26/12, 2012 at 17:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.