More Functions

Consider wanting to write a vector that takes one vector as input and returns a new one.

We can imagine:

std::vector<double> f(const std::vector<double>& v_in) {

    std::vector<double> v_out;

    // do stuff to fill v_out based on v_in

    return v_out;

}

This looks like it is returning v_out by value and that when we do:

auto v_new = f(v_old);

that we need to make a copy. But C++ instead provides move semantics—this means that instead of copying the entire contents of the local v_out to the vector in the caller v_new it simply moves the data by setting the pointer to the data region in v_new to the data region in v_out before v_out is destroyed.

A second method is to pass it through the argument list as a reference:

void f(const std::vector<double>& v_in, std::vector<double>& v_out) {

    // fill v_out based on v_in

}

Then we can do:

std::vector<double> v_old{};
std::vector<double> v_new{};

f(v_old, v_new);

Danger

What about returning a reference? We might think that we could do:

std::vector<double>& f(const std::vector<double>& v_in) {

    std::vector<double> v_out;

    // do stuff to fill v_out based on v_in

    return v_out;

}

The problem here is that v_out is destroyed at the end of the function f, so the reference will be to something that no longer exists. This is not allowed—we cannot return a reference to a local variable.

Let’s play with this:

Listing 32 function_vector.cpp
#include <iostream>
#include <vector>

std::vector<double> f1(const std::vector<double>& v_in) {

    std::vector<double> v_out;

    for (auto e : v_in) {
        v_out.push_back(2.0 * e);
    }

    return v_out;

}

void f2(const std::vector<double>& v_in, std::vector<double>& v_out) {

    v_out.clear();

    for (auto e : v_in) {
        v_out.push_back(2.0 * e);
    }

}

std::vector<double>& f3(const std::vector<double>& v_in) {

    std::vector<double> v_out;

    for (auto e : v_in) {
        v_out.push_back(2.0 * e);
    }

    return v_out;

}

int main() {

    std::vector<double> v_old{0.0, 1.0, 2.0, 3.0, 4.0, 5.0};

    auto v_new1 = f1(v_old);

    for (auto e : v_new1) {
        std::cout << e << " ";
    }
    std::cout << std::endl;

    std::vector<double> v_new2{};

    f2(v_old, v_new2);

    for (auto e : v_new2) {
        std::cout << e << " ";
    }
    std::cout << std::endl;

    auto v_new3 = f3(v_old);

    for (auto e : v_new3) {
        std::cout << e << " ";
    }
    std::cout << std::endl;


}