为什么我宁愿将 std::span 而不是 std::vector& 传递给函数?

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

我知道这可能与问题 重叠,但我认为问题的这个特定部分的答案非常混乱。一方面,有这样的引用:

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.

但是在同一个答案中,会出现这样的说法:

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!

那么我没有到达这里的哪一部分?我什么时候这样做:

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

什么时候呢?

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

还有,这个

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

有什么意义吗?我认为它不应该,因为 std::span 只是一个包含指针和长度的 struct。但是,如果它不能阻止您更改作为参数传递的 std::vector 的值,它如何替换 const std::vector<T>&?

传递 std::vector<int> const& 的等价物不是 std::span<int> const,而是 std::span<int const>。 span 本身是否为 const 不会真正改变任何东西,但更多 const 肯定是好的做法。

那么你应该什么时候使用它?

我会说这完全取决于函数体,您在示例中省略了它。

例如,我仍然会为这种函数传递一个向量:

std::vector<int> stored_vec;

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

这个函数确实存储了向量,所以它需要一个向量。这是另一个例子:

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

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

如您所见,我们需要一个向量。使用跨度,您将必须创建一个新向量并因此进行分配。


对于这种函数,我会传递一个跨度:

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

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

    return total;
}

如您所见,此函数不需要向量。

即使您需要改变范围内的值,您仍然可以使用 span:

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

对于 getter 之类的东西,我也倾向于使用跨度,以免直接引用来自 class:

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

private:
    std::vector<int> vec;
};

关于传递 &std::vector 和传递 std::span 的区别,我可以想到两个重要的事情:

  • std::span 允许您只传递您希望函数查看或修改的数据,而不是整个向量(并且您不必传递起始索引和结束索引)。我发现这对于保持代码清洁非常必要。毕竟,为什么要让一个函数访问比它需要的更多的数据?
  • std::span 可以从多种类型的容器中获取数据(例如 std::arraystd::vector、C 风格的数组)。

这当然也可以通过传递 C 样式数组来完成 - std::span 只是 C 样式数组的包装器,增加了一些安全性和便利性。