将 C 风格数组传递给 `span<T>`

Ran*_*tep 31 c++ c++20

C++20 引入了std::span,它是一个类似视图的对象,可以接受连续序列,例如 C 风格的数组、std::array、 和std::vector。C 风格数组的一个常见问题是在传递给函数时它会衰减为指针。这样的问题可以通过使用来解决std::span

size_t size(std::span<int> s)
{
    return s.size();
}

int main()
{
    std::array arr = {1,2,3,4,5};
    std::vector vec = {1,2,3,4,5};
    auto il = {1,2,3,4,5};
    int c_arr[] = {1,2,3,4,5};
    std::cout << size(arr) << size(vec) << size(il) << size(c_arr);
}
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,这将打印出来5555。然而,size可能不应该只接受int. 相反,它应该容纳任何类型的容器。但是,将 更改size为接受 a 的模板化函数后std::span<T>,它无法再成功替换 C 样式数组,而其他函数则可以:

template<typename T>
size_t size(std::span<T> s)
{
    return s.size();
}

int main()
{
    std::array arr = {1,2,3,4,5};
    std::vector vec = {1,2,3,4,5};
    auto il = {1,2,3,4,5};
    int c_arr[] = {1,2,3,4,5};
    std::cout << size(arr) << size(vec) << size(il) << size(c_arr);
                                                       ^^^^^^^^^^^
    // error: no matching function for call to 'size(int [5])'
    // note: template argument deduction/substitution failed:
    // note: mismatched types 'std::span<_Type, 18446744073709551615>' and 'int*'
}
Run Code Online (Sandbox Code Playgroud)

神箭

这是正确的行为吗?如果是这样,有没有办法接受 C 风格数组span<T>

小智 38

问题不是为什么这对 失败int[],而是为什么它对所有其他类型都有效!不幸的是,您已经成为ADL 的牺牲品,它实际上正在调用std::size而不是size您编写的函数。这是因为函数的所有重载都会失败,因此它会在第一个参数的命名空间中查找匹配的函数,并在其中找到std::size. 重新运行您的程序,并将函数重命名为其他名称:

template<typename T>
size_t my_size(std::span<T> s)
{
    return s.size();
}
Run Code Online (Sandbox Code Playgroud)

在 GCC 12 上我得到

template<typename T>
size_t my_size(std::span<T> s)
{
    return s.size();
}
Run Code Online (Sandbox Code Playgroud)

加上所有其他类型的类似错误。如果您的问题是为什么会失败;那么简短的答案是模板类型推导比常规类型推导要严格得多,因此除非您给出精确匹配(或多或少),否则它将失败。有关更详细的解释,请阅读类似的问题,例如处理类似问题的问题。


Jor*_*LDB 16

好吧,正如多米尼克所说,您编写的函数不适用于任何这些类型。您可以尝试传递从容器创建的跨度,如下所示:

std::cout << getSize(std::span(arr));
std::cout << getSize(std::span(vec.begin(), vec.end()));
std::cout << getSize(std::span(il.begin(), il.end()));
std::cout << getSize(std::span(c_arr));
Run Code Online (Sandbox Code Playgroud)

或者,您也可以为 C 样式数组和标准容器模板化您的函数:

// For std containers
template<class T>
size_t getSize(T myContainer)
{
    return std::span(myContainer.begin(), myContainer.end()).size();
}

// For c-style arrays
template<typename T, int n>
size_t getSize(T (&myArray)[n])
{
    return std::span<T>(myArray).size();
}
Run Code Online (Sandbox Code Playgroud)