将 C 风格的数组传递给 `span<T>`
Passing a C-style array to `span<T>`
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);
}
这将按预期打印 5555
。但是,size
可能不应该只接收 int
的容器。相反,它应该接受任何类型的容器。但是,将 size
更改为接受 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*'
}
这是正确的行为吗?如果是这样,有没有办法接受带有 span<T>
的 C 样式数组?
问题不在于为什么 int[]
会失败,而在于为什么它适用于所有其他类型!不幸的是,您已成为 ADL 的牺牲品,它实际上调用的是 std::size
而不是您编写的 size
函数。这是因为您的函数的所有重载都失败了,因此它会在第一个参数的命名空间中查找匹配函数,并在其中找到 std::size
。重新运行您的程序,将函数重命名为其他名称:
template<typename T>
size_t my_size(std::span<T> s)
{
return s.size();
}
在 GCC 12 上我得到
prog.cc:18:25: error: no matching function for call to 'my_size(std::array<int, 5>&)'
18 | std::cout << my_size(arr) << my_size(vec) << my_size(il) << my_size(c_arr);
| ~~~~~~~^~~~~
prog.cc:7:8: note: candidate: 'template<class T> size_t my_size(std::span<_Type, 18446744073709551615>)'
7 | size_t my_size(std::span<T> s)
| ^~~~~~~
prog.cc:7:8: note: template argument deduction/substitution failed:
prog.cc:18:25: note: 'std::array<int, 5>' is not derived from 'std::span<_Type, 18446744073709551615>'
18 | std::cout << my_size(arr) << my_size(vec) << my_size(il) << my_size(c_arr);
| ~~~~~~~^~~~~
加上所有其他类型的类似错误。如果您的问题是为什么会失败;那么简短的回答是模板类型推导比常规类型推导严格得多,所以除非你给它一个完全匹配(或多或少),否则它会失败。有关更详细的解释,请阅读类似的问题,例如 处理类似的问题。
好吧,正如 ,您编写的函数不适用于任何这些类型。您可以尝试传递从您的容器创建的跨度,如下所示:
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));
或者您也可以为 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();
}
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);
}
这将按预期打印 5555
。但是,size
可能不应该只接收 int
的容器。相反,它应该接受任何类型的容器。但是,将 size
更改为接受 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*'
}
这是正确的行为吗?如果是这样,有没有办法接受带有 span<T>
的 C 样式数组?
问题不在于为什么 int[]
会失败,而在于为什么它适用于所有其他类型!不幸的是,您已成为 ADL 的牺牲品,它实际上调用的是 std::size
而不是您编写的 size
函数。这是因为您的函数的所有重载都失败了,因此它会在第一个参数的命名空间中查找匹配函数,并在其中找到 std::size
。重新运行您的程序,将函数重命名为其他名称:
template<typename T>
size_t my_size(std::span<T> s)
{
return s.size();
}
在 GCC 12 上我得到
prog.cc:18:25: error: no matching function for call to 'my_size(std::array<int, 5>&)'
18 | std::cout << my_size(arr) << my_size(vec) << my_size(il) << my_size(c_arr);
| ~~~~~~~^~~~~
prog.cc:7:8: note: candidate: 'template<class T> size_t my_size(std::span<_Type, 18446744073709551615>)'
7 | size_t my_size(std::span<T> s)
| ^~~~~~~
prog.cc:7:8: note: template argument deduction/substitution failed:
prog.cc:18:25: note: 'std::array<int, 5>' is not derived from 'std::span<_Type, 18446744073709551615>'
18 | std::cout << my_size(arr) << my_size(vec) << my_size(il) << my_size(c_arr);
| ~~~~~~~^~~~~
加上所有其他类型的类似错误。如果您的问题是为什么会失败;那么简短的回答是模板类型推导比常规类型推导严格得多,所以除非你给它一个完全匹配(或多或少),否则它会失败。有关更详细的解释,请阅读类似的问题,例如
好吧,正如
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));
或者您也可以为 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();
}