1. 有了std::span
都省了些写template
的功夫了
示例一:
#include <iostream>
#include <span>
#include <vector>
void printNum(std::span<int> view_contiguous_sequence){
for(const auto it: view_contiguous_sequence){
std::cout << it << " ";
}
std::cout << std::endl;
}
int main(){
int arr[] = {1,2,3,4,5,6};
std::span<int> view_arr(arr, 4);
std::span<int> view_arr2(arr,2);
printNum(arr); // 1 2 3 4 5 6
printNum(view_arr); // 1 2 3 4
printNum(view_arr2); // 1 2
std::cout << std::endl;
std::vector<int> vec{9,8,7,6,5,4,3};
std::span<int> view_vec(vec);
std::span<int,5> view_vec1(vec);
std::span<int> view_vec2(vec.begin(), vec.begin() + 3);
std::span<int> view_vec3(vec.begin(), 4);
printNum(vec); // 9 8 7 6 5 4 3
printNum(view_vec); // 9 8 7 6 5 4 3
printNum(view_vec1); // 9 8 7 6 5
printNum(view_vec2); // 9 8 7
printNum(view_vec3); // 9 8 7 6
}
示例二:
#include <iostream>
#include <span>
#include <vector>
#include <array>
#include <list>
struct Obj{
void operator()(const std::span<int> contiguous_equence) const {
for(const auto it: contiguous_equence){
std::cout << it << " ";
}
std::cout << std::endl;
}
}obj;
int main(){
int arr[] = {0,1,2,3,4,5,6,7,8,9};
std::vector<int> vec{1,2,3,4,5,6,7};
std::array<int,5> arra{2,3,4,5,6};
std::list<int> lst{3,4,5,6,7};
std::span</*const*/ int> span1(arr, 4);
obj(span1); // 0 1 2 3
obj(arr); // 0 1 2 3 4 5 6 7 8 9
obj(vec); // 1 2 3 4 5 6 7
obj(arra); // 2 3 4 5 6
// obj(lst); // 不支持std::list,会报错error: no match for call to '(Obj) (std::__cxx11::list<int>&)'
std::cout << std::endl;
/* 修改std::span对象会修改创建std::span的对象 */
span1[2] = 9;
obj(span1); // 0 1 9 3
obj(arr); // 0 1 9 3 4 5 6 7 8 9
}
2. std::span
并不拥有数据
类似std::string_view
并不拥有数据,一定要注意std::span
引用对象的有效性。
如下代码的问题是返回了局部变量arr
的地址,函数调用结束后返回的地址已经无效了。
#include <iostream>
int *test(){
int arr[] = {1,2,3};
int *p = arr;
return p;
}
auto main()->int{
// g++ main.cpp -std=c++20 -fsanitize=address
int* p = test();
std::cout << "*p = " << *p << std::endl;
}
类似上述错误,使用std::span
时务必注意要避免下述做法:
#include <iostream>
#include <span>
#include <vector>
auto main()->int{
// g++ main.cpp -std=c++20 -fsanitize=address
std::span<int> sp1;
{
std::vector<int> vec{1,2,3,4,5,6};
sp1 = std::span<int>(vec.begin() + 1, vec.end() - 1);
} // 打括号结束位置vec已经不存在了,但是sp1还在引用它,属于未定义行为,多线程下更容易发现问题
for(const auto&it: sp1){
// 每次输出的值可能都不一样
std::cout << it << " "; // 0 1884814536 284698390 5
}
std::cout << std::endl;
}
上述代码编译后,运行报错如下:
=================================================================
==1==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000044 at pc 0x0000004018b4 bp 0x7fff0ad7fb30 sp 0x7fff0ad7fb28
READ of size 4 at 0x603000000044 thread T0
#0 0x4018b3 in main /app/example.cpp:36
#1 0x71a82be29d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
#2 0x71a82be29e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
#3 0x401204 in _start (/app/output.s+0x401204) (BuildId: 04a33e88cc2a9de560eb5e605daa0a9d7522f39e)
0x603000000044 is located 4 bytes inside of 24-byte region [0x603000000040,0x603000000058)
freed by thread T0 here:
#0 0x71a82c4b6a78 in operator delete(void*, unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdda78) (BuildId: 53df075b42b04e0fd573977feeb6ac6e330cfaaa)
#1 0x403730 in std::__new_allocator<int>::deallocate(int*, unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/new_allocator.h:168
#2 0x403256 in std::allocator<int>::deallocate(int*, unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/allocator.h:210
#3 0x403256 in std::allocator_traits<std::allocator<int> >::deallocate(std::allocator<int>&, int*, unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/alloc_traits.h:516
#4 0x403256 in std::_Vector_base<int, std::allocator<int> >::_M_deallocate(int*, unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:387
#5 0x402712 in std::_Vector_base<int, std::allocator<int> >::~_Vector_base() /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:366
#6 0x402881 in std::vector<int, std::allocator<int> >::~vector() /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:735
#7 0x40179d in main /app/example.cpp:32
#8 0x71a82be29d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
previously allocated by thread T0 here:
#0 0x71a82c4b5b78 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdcb78) (BuildId: 53df075b42b04e0fd573977feeb6ac6e330cfaaa)
#1 0x4027e3 in std::__new_allocator<int>::allocate(unsigned long, void const*) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/new_allocator.h:147
#2 0x402543 in std::allocator<int>::allocate(unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/allocator.h:198
#3 0x402543 in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/alloc_traits.h:482
#4 0x402543 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:378
#5 0x401f1a in void std::vector<int, std::allocator<int> >::_M_range_initialize<int const*>(int const*, int const*, std::forward_iterator_tag) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:1689
#6 0x401b94 in std::vector<int, std::allocator<int> >::vector(std::initializer_list<int>, std::allocator<int> const&) /opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_vector.h:679
#7 0x4015d2 in main /app/example.cpp:30
#8 0x71a82be29d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 962015aa9d133c6cbcfb31ec300596d7f44d3348)
SUMMARY: AddressSanitizer: heap-use-after-free /app/example.cpp:36 in main
Shadow bytes around the buggy address:
0x602ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x602ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x602ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x602fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x602fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x603000000000: fa fa 00 00 00 fa fa fa[fd]fd fd fa fa fa fa fa
0x603000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x603000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x603000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x603000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x603000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1==ABORTING