Home > Net >  c how to use return value of function immediately in another function as reference
c how to use return value of function immediately in another function as reference

Time:05-25

I have the following code to try to combine vectors by chaining a call to function combine. I'm getting error: initial value of reference to non-const must be an lvalue

#include <algorithm>
using namespace std;

vector<int> combine(vector<int>& v1, vector<int>& v2) {
    vector<int> v;
    for(int ele : v1) v.push_back(ele);
    for(int ele : v2) v.push_back(ele);
    return v;
}

int main() {
    vector<int> v1 = {1,2,3};
    vector<int> v2 = {4,5,6};
    vector<int> v3 = {7,8,9};
    combine(v1, combine(v2, v3));
}

Why is this, and what is the correct workaround?

I am afraid that doing this might cause unnecessary copying:

vector<int> temp = combine(v2, v3);
combine(v1, temp);

CodePudding user response:

Your combine() function is returning a vector by value, which means it returns a temporary object that is destroyed as soon as the caller is done using it. As such, the function is returning an rvalue, and an rvalue cannot bind to a reference to a non-const object, as you are trying to do, hence the error.

Make your function parameters be references to const objects instead. An rvalue can bind to a const reference. And besides, your function is not modifying the input vectors, so they should be const anyway.

Try this:

#include <algorithm>
using namespace std;

//                   \/                     \/
vector<int> combine(const vector<int>& v1, const vector<int>& v2) {
    vector<int> v;

    // you should add this for good measure:
    // v.reserve(v1.size()   v2.size());

    for(int ele : v1) v.push_back(ele);
    for(int ele : v2) v.push_back(ele);

    // alternatively:
    // v.insert(v.end(), v1.begin(), v1.end());
    // v.insert(v.end(), v2.begin(), v2.end());

    return v;
}

int main() {
    vector<int> v1 = {1,2,3};
    vector<int> v2 = {4,5,6};
    vector<int> v3 = {7,8,9};

    // now this will work as expected...
    vector<int> v4 = combine(v1, combine(v2, v3));

    for(int ele : v4) {
        cout << ele << ' ';
    }
}

Output:

1 2 3 4 5 6 7 8 9

CodePudding user response:

Making the arguments to combine const solves the original problem:

vector<int> combine(const vector<int>& v1, const vector<int>& v2)

But having to call combine on pairs of vectors is both inefficient and too much to type. Here is my take on this:

#include <vector>
#include <concepts>

// make a variadic function so all the combining can be done in one call     
template <typename T, typename... U>
requires (std::same_as<U , std::vector<T>> && ...)         // all U must be vectors of T
std::vector<T> combine(std::vector<T>&& v, U... vs) {      // allow move semantic for arguments
    std::vector<T> res(v.size()   (vs.size()   ...));      // make vector big enough to hold the result
    for(auto && ele : v) res.emplace_back(std::move(ele)); // copy/move first vector
    ([&res](auto && t) {                                   // copy/move the rest
        for(auto && ele : t) res.emplace_back(std::move(ele));
    }(vs), ...);
    return res;                                            // return result with copy elision
}

int main() {
    std::vector<int> v1 = {1,2,3};
    std::vector<int> v2 = {4,5,6};
    std::vector<int> v3 = {7,8,9};
    std::vector<int> c = combine(v1, v2, v3);
}
  • Related