为什么我宁愿将 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::array
、std::vector
、C 风格的数组)。
这当然也可以通过传递 C 样式数组来完成 - std::span
只是 C 样式数组的包装器,增加了一些安全性和便利性。
我知道这可能与问题
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::array
、std::vector
、C 风格的数组)。
这当然也可以通过传递 C 样式数组来完成 - std::span
只是 C 样式数组的包装器,增加了一些安全性和便利性。