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)