Given the following code, where x
is a dangling const reference
to a vanished object, and is therefore undefined behavior.
auto get_vec() { return std::vector<int>{1,2,3,4,5}; }
const auto& x = get_vec().back();
It seems like neither GCC 7.3, Clang 6.0 and MSVC is able to emit a warning, even with all warnings enabled.
Does anyone know if it is any way to emit a warning in these cases?
Is there any difference between const auto&
and auto&&
in these cases?
Note, if back()
would return by value, it wouldn't be undefined behavior as the lifetime temporary object x is extended to function scoop.
Long story: I have a code base where const auto&
is used as the default way of initializing variables, and for some odd reason these cases executes correctly using MSVC, but when compiled with Clang for android, every occurance results in a wrongly assigned value. For now the solution seems to investigate every const auto&
in the whole code base.
Also, in many cases the const auto&
refers to a heavy object returned by reference so simply removing the &
is not a solution.
One more thing, I'm responsible for the miss use of const auto&
:)
Only thing I can come up with right now is to use CLANG with -fsanitize=address. But of course this will only help at runtime, but then you get something nice like this:
==102554==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000020 at pc 0x00000050db71 bp 0x7ffdd3a5b770 sp 0x7ffdd3a5b768
READ of size 4 at 0x603000000020 thread T0
#0 0x50db70 in main (/home/user/testDang+0x50db70)
#1 0x1470fb404889 in __libc_start_main (/lib64/libc.so.6+0x20889)
#2 0x41a019 in _start (/home/user/testDang+0x41a019)
0x603000000020 is located 16 bytes inside of 20-byte region [0x603000000010,0x603000000024)
freed by thread T0 here:
#0 0x50a290 in operator delete(void*) (/home/user/testDang+0x50a290)
#1 0x50eccf in __gnu_cxx::new_allocator<int>::deallocate(int*, unsigned long) (/home/user/testDang+0x50eccf)
#2 0x50ec9f in std::allocator_traits<std::allocator<int> >::deallocate(std::allocator<int>&, int*, unsigned long) (/home/user/testDang+0x50ec9f)
#3 0x50ec2a in std::_Vector_base<int, std::allocator<int> >::_M_deallocate(int*, unsigned long) (/home/user/testDang+0x50ec2a)
#4 0x50e577 in std::_Vector_base<int, std::allocator<int> >::~_Vector_base() (/home/user/testDang+0x50e577)
#5 0x50e210 in std::vector<int, std::allocator<int> >::~vector() (/home/user/testDang+0x50e210)
#6 0x50db16 in main (/home/user/testDang+0x50db16)
#7 0x1470fb404889 in __libc_start_main (/lib64/libc.so.6+0x20889)
previously allocated by thread T0 here:
#0 0x509590 in operator new(unsigned long) (/home/user/testDang+0x509590)
#1 0x50e9ab in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (/home/user/testDang+0x50e9ab)
#2 0x50e94b in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (/home/user/testDang+0x50e94b)
#3 0x50e872 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (/home/user/testDang+0x50e872)
#4 0x50e2ff in void std::vector<int, std::allocator<int> >::_M_range_initialize<int const*>(int const*, int const*, std::forward_iterator_tag) (/home/user/testDang+0x50e2ff)
#5 0x50deb7 in std::vector<int, std::allocator<int> >::vector(std::initializer_list<int>, std::allocator<int> const&) (/home/user/testDang+0x50deb7)
#6 0x50dafb in main (/home/user/testDang+0x50dafb)
#7 0x1470fb404889 in __libc_start_main (/lib64/libc.so.6+0x20889)
SUMMARY: AddressSanitizer: heap-use-after-free (/home/user/testDang+0x50db70) in main
Shadow bytes around the buggy address:
0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c067fff8000: fa fa fd fd[fd]fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8050: 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
Maybe you have automated unit tests you can easily run as "sanizizer" builds.