0

If I want to return some struct in the way of structured return. Like

pair<bool, vector<int>> get(const string &id) {
    vector<int> v;
    return {true, v};
}

or

struct st{};
pair<bool, st> get(const string &id) {
    st v;
    return {true, v};
}

and using like

auto [a1, b1] = get("1");   ---version1
auto &[a2, b2] = get("1");   ---version2

So I have two doubts in it mainly considering of the cost of copy construct. Q1: Is there difference between version1 and version2 in or outside my case of calling get? Q2: There always needs one time of copy construct in copying into the returned pair, but I think it is unecessary at least in my case. So is there some ways to save this time of copy construct?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
f1msch
  • 509
  • 2
  • 12
  • 1
    If a pair object is created, then it will contain copies of the data you initialize the pair with. And since the pair will be very temporary and destructed immediately, then "version2" will get you dangling references. – Some programmer dude Mar 27 '23 at 11:38
  • @Someprogrammerdude Really? The reference extends the lifetime of the returned value. Both versions are fine. I would not expect any difference in performance between the two, either, due to guaranteed copy elision of the return value. – j6t Mar 27 '23 at 12:03
  • I guess you are trying to apply some `move` semantic instead of a copy. [This thread](https://stackoverflow.com/questions/4097536/using-move-semantics-with-stdpair-or-stdtuple) may help you. In your case, you may want to do something like `return std::move({true, std::move(v)})`. I'm nothing like a C++ expert, so don't hesitate correcting me – PoneyUHC Mar 27 '23 at 12:09
  • @j6t Actually, [it should not build](https://godbolt.org/z/rj3h34W5a). The OP needs `auto const&[a2, b2]` for it to extend the life-time and build. – Some programmer dude Mar 27 '23 at 12:13
  • To the OP: As I mentioned above your "version2" should not even build. So that basically leaves you with only one alternative (unless you're satisfied with using `const` references). – Some programmer dude Mar 27 '23 at 12:16
  • Create the pair first and return that. – molbdnilo Mar 27 '23 at 12:53

1 Answers1

1

You need to understand (N)RVO ((Named) Return Value Optimization) and Copy Elision.

Both are well described in the CPP standard here.

In most cases you do not need to fear expensiv copy operations. The compiler will use RVO to help you. However, there are corner cases, when RVO will not work. Again, please read about it.

You may imagine, that the compiler creates space for the return value outside of the function, so in the callers frame/scope. Then it is clear that no copying takes place.

In this respect it is better to create the return value first, and then return it. Then RVO can happen.

You may run into trouble with returning references to function local variables. This may create dangling references. (references to function local variables that go out of scope, after the function ends). If you want to use this, then you can use const references.

So, you may use both of

#include <utility>
#include <string>
#include <vector>
using namespace std::string_literals;

std::pair<bool, std::vector<int>> get1(const std::string &id) {
    std::pair<bool, std::vector<int>> result{true, {}};
    return result;
}

struct st{};
std::pair<bool, st> get2(const std::string& id) {
    std::pair<bool, st> result{true, {}};
    return result;
}

int main() {
    const auto& [a1, b1] = get1("1"s);
    auto [a2, b2] = get1("1"s);

    const auto& [c1, d1] = get2("1"s);
    auto [c2, d2] = get2("1"s);

    return a1 and a2 and c1 and c2;
}

The recommendation is to go with the const version, if possible.

Look at the output on Compiler Explorer

And see what Clang does :-)

A M
  • 14,694
  • 5
  • 19
  • 44