简体   繁体   English

标准::跨度<const t>作为 function 模板中的参数</const>

[英]std::span<const T> as parameter in function template

In the following library functions f and g , I use std::span<const T> to remind the user of the library of the contract that f and g will not modify the contents of the span.在下面的库函数fg中,我使用std::span<const T>来提醒合约库的用户fg不会修改 span 的内容。 The user of the library holds std::span<int> a , which is convertible to std::span<const int> .该库的用户持有std::span<int> a ,可转换为std::span<const int> Hence, the user can call g(a) as expected.因此,用户可以按预期调用g(a) However, the user cannot call f(a) , since f is a function template and a is not of the required type;但是,用户不能调用f(a) ,因为f是 function 模板并且a不是所需的类型; conversions do not apply here.转换不适用于此处。 Can you see a (sane) way to have f take in std::span<const T> while still accepting std::span<T> ?你能看到一种(理智的)方式让f接受std::span<const T>同时仍然接受std::span<T>吗?

#include <span>

void g(const std::span<const int>& a) {}

template <typename T>
void f(const std::span<const T>& a) {}

int main()
{
    std::span<int> a;
    // OK
    g(a);
    // No match
    f(a);
    // OK
    f((std::span<const int>)a);
}

It is possible to add an overload like this:可以像这样添加重载:

template <typename T>
void f(const std::span<T>& a) {
    return f((std::span<const T>)a);
}

But I'm not sure if I count this as sane, since then I would be writing these overloads to every function template which takes in std::span<const T> .但我不确定我是否认为这是理智的,从那时起我会将这些重载写入每个接受std::span<const T>的 function 模板。

EDIT: Not the same case, but if f also happens to take in another parameter which mentions T , then one can do the following:编辑:不一样的情况,但如果f也碰巧接受另一个提到T的参数,那么可以执行以下操作:

template <typename T> using NoDeduction = std::type_identity_t<T>;

template <typename T>
void f(NoDeduction<std::span<const T>> a, const T& b) {}

After which f(a, 1);之后f(a, 1); works.作品。 This is a partial solution.这是部分解决方案。 The original problem still remains.原来的问题依然存在。

EDIT 2: I was wondering whether the above technique works also when we include span's compile-time size:编辑 2:我想知道当我们包含 span 的编译时大小时,上述技术是否也有效:

template <typename T, std::size_t N>
void f(const std::span<NoDeduction<const T>, N>& a, const T& b) {}

Gcc 10.1 compiles it, MSVC 16.6.2 and Clang 10.0.0 don't. Gcc 10.1 编译它,MSVC 16.6.2 和 Clang 10.0.0 不编译。 I filed bug reports for both MSVC and Clang.我提交了 MSVC 和 Clang 的错误报告。

You can generalize your overload to be a utility like std::as_const :您可以将您的重载概括为像std::as_const这样的实用程序

template<class T>
std::span<const T> const_span(std::span<T> s) {return s;}

The caller then has to decorate their calls with mutable spans, but in so doing indicates to the reader that no modification is allowed.然后调用者必须用可变跨度装饰他们的调用,但这样做会向读者表明不允许修改。

Another, unconventional workaround would be to make your function be a class and use deduction guides :另一种非常规的解决方法是使您的 function 成为 class 并使用扣除指南

template<class T>
struct f {
  f(std::span<const T>);
  // operator ReturnType();
};
template<class T> f(std::span<T>)->f<T>;

It's debatable whether the interface describes the intent here.接口是否描述了这里的意图是有争议的。

It is unable to make the type conversion because it fails to deduce the type as f is a template.它无法进行类型转换,因为它无法推断类型,因为f是模板。

If you want to write template function f that accepts various input types - you'd better write it as如果你想编写接受各种输入类型的模板 function f - 你最好把它写成

 template<typename Viewer>
 void f(const Viewer& a){...}

If you want to work with span then you can implement it in two steps:如果您想使用span ,那么您可以分两步实现它:

 template<typename T>
 void f_impl(std::span<const T> a){...}

 template<typename Viewer>
 void f(const Viewer& a)
 {
      f_impl(std::span<const typename Viewer::value_type>(a.data(),a.size()));
  }

PS with span you should normally take copy of it instead of "const reference". PS with span你通常应该复制它而不是“const reference”。 This is more efficient.这样更有效率。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM