0

我想将我的用户定义概念用作 std::span 的模板类型,但模板参数推导无法按预期工作。当我尝试将“ std::arrayof char”传递给模板函数时;编译器显示错误“ error: no matching function for call to 'print' ”并在我将鼠标悬停在模板定义上时警告我“注意:候选模板被忽略:无法匹配 'span' 与 'array'”

这是概念定义和功能模板:

#include <concepts>
#include <span>

template <typename  T>
concept OneByteData = sizeof(T) == 1;

template<OneByteData T>
void print(std::span<const T> container)
{
    for(auto element : container)
    {
        //Do Some Work
    }
}

并且无法按预期工作的用户代码:

int main()
{
    std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
    print(arr);
    return 0;
}

有效且不产生错误的用户代码:

int main()
{
    std::array<char, 6> arr = {1, 2, 3, 4, 5, 6};
    print<char>(arr);
    return 0;
}

有没有办法在不专门指定数组类型的情况下调用这个模板函数。我应该如何更改模板函数定义以使函数以我提到的方式调用 ( print(arr)) ?

编辑:我希望能够使用 std::span 的好处,并能够使用 std::array、std::vector 和纯 C 样式数组调用模板函数。

4

2 回答 2

1

一个可能的解决方案是接收(并推断)一个泛型类型,然后检查它是否可转换为std::span大小element_type为 1 的 a。

我是说

template <typename  T>
concept OneByteData = sizeof(T) == 1;

template <typename T>
void print (T container) 
   requires OneByteData<typename decltype(std::span{container})::element_type>
{
  std::span cnt {container};
  
    for(auto element : cnt )
    {
        //Do Some Work
    }
}

// extra print for C-style cases
template <typename T, std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }

另一种可能的解决方案是 a print(),类似于您的原始解决方案,它接收 astd::span并进行概念检查,以及一些额外的print()(一个特定于 C 样式数组)将推导的类型转换为std::span

template <typename  T>
concept OneByteData = sizeof(T) == 1;

template<OneByteData T, std::size_t N>
void print(std::span<T, N> container)
{
    for(auto element : container)
    {
        //Do Some Work
    }
}

template <typename T, std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }

template <typename T>
void print (T container)
 { print(std::span{container}); }
于 2021-05-02T11:42:54.440 回答
0

据我了解,这就是你应该如何实际使用它:

// Second template parameter for size
template<OneByteData T, std::size_t N>
void print(std::span<T, N> container) {
  // Do something
  return;
}

// Create a span from a plain array
print(std::span{arr});

在这里试试


如果你不希望你的用户自己做,你可以

  • 为处理从到的转换编写一个重载,print例如(Wandboxstd::arraystd::span

    template<OneByteData T, std::size_t N>
    void print(std::array<T,N> const& container) {
      return print<T const>(std::span{container});
    }
    
  • 否则,您可以重写接口print获取任何容器std::enable_if,使用用户 max66 提出的概念对底层元素类型强制执行约束,并将此通用容器转换为std::span内部容器。

  • 对于一个,您可以编写一个模板推导指南,该指南决定应该使用哪个构造函数(Wandbox

    template <typename T, std::size_t N>
    Print(std::array<T,N>) -> Print<T,N>;
    

编辑

对于像评论中讨论的那样的运算符,我实际上会使用模板化函数和模板化运算符的组合。该函数append使用数据类型的泛型完成工作std::spanOneByteData而模板化运算符将允许的数据类型转换为std::span并调用该函数。OneByteData<typename decltype(std::span{cont})::element_type>确保可以将数据结构转换为std::span具有正确数据类型的 a。您可以为其添加额外的或不同的约束,或者将这些约束结合到您自己的概念中。

template<OneByteData T, std::size_t N>
void append(std::span<T,N> const& sp) {
  // Do something
}

template <typename Cont>
MyString& operator += (Cont const& cont) 
requires OneByteData<typename decltype(std::span{cont})::element_type> {
  this->append(std::span{cont});
  return *this;
}

在这里这里试试!

于 2021-05-02T11:30:00.397 回答