我定义了以下函数:
template<
std::ranges::contiguous_range R,
typename T = std::ranges::range_value_t<R>
>
std::span<T> foo(R&& r, const T& someValue) {
std::span<T> sp{r.begin(), r.end()};
/// ...
return sp;
}
现在我有三个用例:
std::vector<std::string> baseVec;
auto a = foo(baseVec, {""});
std::span<std::string> sp{baseVec};
auto b = foo(sp, {""});
const std::vector<std::string>& ref = baseVec;
auto c = foo(ref, {""}); // <------------------ problem here!
据我所知,foo(ref)
将无法编译,因为span
内部创建的foo
类型是span<T>
,而在这种情况下它应该是span<const T>
。
那么,我该如何编写foo
才能让它接受所有这三种情况?
问题是
value_type
范围的 永远不是const
- 限定的。但reference
范围的类型可以是 - 虽然这仍然是错误的,因为它将是string const&
。连续迭代器的引用类型需要为
U&
某种类型U
,因此如果我们简单地删除尾随引用,我们将得到一个潜在的 const 限定类型:请注意,您不需要将
r.begin()
和r.end()
传递到span
,span
有一个范围构造函数。但是,这仍然不完全正确,因为原文也是错误的。
contiguous
这里的标准不够充分,我们还需要范围,sized
以便我们可以构造一个span
:另外,您可能不想
T
从参数中推断出来 - 您真正希望的是它T
与范围具体正确相关:请注意,我修改了您的代码,使用括号而不是大括号来构造
span
。这是因为使用大括号将迭代器对传递到初始化程序中不是一个好主意,因为它很容易做错事。考虑一下:同样的情况也发生在 C++26 中,
span
它将拥有一个构造函数。initializer_list
你可以走俗套路。如果编译器这么聪明,就让它自己找出类型。使用类模板参数推导,这是可能的。