为什么在传递 std::vector 时不能为 std::span<T> 推断 T?

Why can T not be deduced for std::span<T> when passing a std::vector?

提问人:FrankM 提问时间:9/1/2023 最后编辑:Jan SchultkeFrankM 更新时间:9/13/2023 访问量:283

问:

在下面的 C++20 代码中,将 传递给带有参数的模板化函数会失败,因为编译器显然无法推断模板参数。我已经用 GCC、Clang 和 MSVC 尝试过了;都失败了。std::vectorstd::span<T>

像这样调用有效:或 .f3(std::span(vi))f3(std::span(vp))

我想知道为什么会失败,因为在我的理解中,是一个范围,并且有一个范围的推导。std::vectorstd::span

#include <memory>
#include <vector>
#include <span>

void f1(std::span<int> s)
{
}

void f2(std::span<std::shared_ptr<int>> s)
{
}

template<typename T>
void f3(std::span<T> s)
{
}

int main(int argc, char* argv[])
{
    std::vector<int> vi;
    std::vector<std::shared_ptr<int>> vp;

    f1(vi);
    f2(vp);
    f3(vi); // ERROR: no matching function for call to 'f3'
    f3(vp); // ERROR: no matching function for call to 'f3'

    return 0;
}
C++ C++20 模板参数推导 std-span

评论


答:

17赞 HolyBlackCat 9/1/2023 #1

如果函数参数参与模板参数推导,则不允许对该函数参数进行隐式转换。

评论

0赞 FrankM 9/1/2023
为什么范围的 std::span 扣除指南不起作用?难道不应该允许这种转换吗?
0赞 HolyBlackCat 9/1/2023
@FrankM 我不完全理解这条规则背后的原理,但不,它似乎没有扣除指南的例外。
3赞 HolyBlackCat 9/1/2023
@FrankM 如果您需要一个实用的解决方案,也许只需接受 ,然后与 SFINAE 联系,确认它是否可以转换为 ,以及用于哪种元素类型。T &&rangestd::span<??>
0赞 463035818_is_not_an_ai 9/1/2023
如果有另一个并且还有一个从向量构建的演绎指南怎么办?...好吧,那么这可能是一个歧义错误。也许还没有人提出这样的例外。template <typename T> void f3(foo<T>)foo
1赞 Toby Speight 9/1/2023
@HolyBlackCat,为什么不是约束而不是 SFINAE?
5赞 Jan Schultke 9/1/2023 #2

不幸的是,你要做的事情是不可能的。在通话中....

template<typename T>
void f3(std::span<T> s) { /* ... */ }
// [...]

std::vector<int> vi;
f3(vi);

...不是 ,因此无法从中推导出模板参数。函数模板参数推导不考虑隐式转换。std::vectorvistd::spanT

您已经指出有一个扣除指南,但该功能不能在这里使用:

模板名称显示为推导类类型的类型说明符时,将使用推导指南。

- [温度扣除指南]

这意味着 std::span 推导指南......

template< class R >
span( R&& ) -> span<std::remove_reference_t<std::ranges::range_reference_t<R>>>;

...可用于以下场景:

f3(std::span(vi));
// or
std::span s = vi;

无论哪种情况,这都会形成一个 .std::span<int>

溶液

总的来说,这是一个众所周知且非常烦人的局限性。如果你想要一个“通用范围”,惯用的解决方案是接受一个受约束的范围:std::span

#include <ranges>

template <std::ranges::contiguous_range R>
  requires /* TODO: additional constraints for the range value type? */
void foo(R&& r) {
    // ...
}

由于您无论如何都在编写函数模板,因此制作一个接受任何 std::ranges::contiguous_range 的模板与编写一个只接受 .std::span

大多数情况下,您可以使用更宽松的范围要求,例如 std::ranges::forward_range

0赞 康桓瑋 9/1/2023 #3

我想知道为什么这失败了,因为在我的理解中,是一个范围,并且有一个扣除指南 范围。std::vectorstd::span

这个答案解释了为什么这不起作用。如果您仍想工作,可以通过检查类型是否启用以下类型的扣除指南来实现:f3(vi)Rstd::span

#include <span>

template<class R>
  requires requires(R&& r) { std::span(std::forward<R>(r)); }
void use_as_span(R&& r) {
  std::span sp(std::forward<R>(r));
  // use span here
}

演示