2

我知道这可能与什么是“跨度”以及何时应该使用“跨度”这个问题重叠?,但我认为问题的这个特定部分的答案非常令人困惑。一方面,有这样的引用:

如果您有一个您知道适合您的代码的标准库容器(或 Boost 容器等),请不要使用它。它并不打算取代其中任何一个。

但在同一个答案中,出现了以下语句:

当您希望数据在内存中连续时,将 const vector& 传递给函数是合理的替代方法。再也不用被高大上的 C++ 大师骂了!

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

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

什么时候?

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

还有,这会

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

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

4

2 回答 2

4

相当于传递 astd::vector<int> const&的不是std::span<int> const,而是std::span<int const>。跨度本身是否为 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 之类的东西,我也倾向于使用 span,以免暴露对类成员的直接引用:

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

private:
    std::vector<int> vec;
};
于 2020-12-15T20:03:34.403 回答
2

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

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

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

于 2020-12-15T20:28:00.460 回答