Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in particular should I rather pass a std::span than a std::vector& to a function?

I know this might overlap with the question What is a “span” and when should I use one?, but I think the answer to this specific part of the question is pretty confusing. On one hand, there are quotes like this:

Don't use it if you have a standard library container (or a Boost container etc.) which you know is the right fit for your code. It's not intended to supplant any of them.

But in the same answer, this statement occurs:

is the reasonable alternative to passing const vector& to functions when you expect your data to be contiguous in memory. No more getting scolded by high-and-mighty C++ gurus!

So what part am I not getting here? When would I do this:

void foo(const std::vector<int>& vec) {}

And when this?

void foo(std::span<int> sp) {}

Also, would this

void foo(const std::span<int> sp) {}

make any sense? I figured that it shouldn't, because a std::span is just a struct, containing a pointer and the length. But if it doesn't prevent you from changing the values of the std::vector you passed as an argument, how can it replace a const std::vector<T>&?

like image 321
Tom Gebel Avatar asked Nov 23 '25 02:11

Tom Gebel


1 Answers

The equivalent of passing a std::vector<int> const& is not std::span<int> const, but rather std::span<int const>. The span itself being const or not won't really change anything, but more const is certainly good practice.

So when should you use it?

I would say that it entirely depends on the body of the function, which you omitted from your examples.

For example, I would still pass a vector around for this kind of functions:

std::vector<int> stored_vec;

void store(std::vector<int> vec) {
    stored_vec = std::move(vec);
}

This function does store the vector, so it needs a vector. Here's another example:

void needs_vector(std::vector<int> const&);

void foo(std::vector<int> const& vec) {
    needs_vector(vec);
}

As you can see, we need a vector. With a span you would have to create a new vector and therefore allocate.


For this kind of functions, I would pass a span:

auto array_sum(std::span<int const> const values) -> int {
    auto total = int{0};

    for (auto const v : values) {
        total += v;
    }

    return total;
}

As you can see, this function don't need a vector.

Even if you need to mutate the values in the range, you can still use span:

void increment(std::span<int> const values) {
    for (auto& v : values) {
        ++v;
    }
}

For things like getter, I will tend to use a span too, in order to not expose direct references to members from the class:

struct Bar {
    auto get_vec() const -> std::span<int const> {
        return vec;
    }

private:
    std::vector<int> vec;
};
like image 59
Guillaume Racicot Avatar answered Nov 24 '25 16:11

Guillaume Racicot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!