Правильно ли удалять элементы с помощью std :: list :: remove, используя псевдоним элемента в контейнере? - PullRequest
6 голосов
/ 20 сентября 2019

Когда я компилирую эту программу:

#include <list>

int main() {
    std::list<int> l = {1, 2};
    l.remove(l.front());
}

С помощью clang с использованием ASAN и отладкой:

clang++-8 -fno-omit-frame-pointer -g -fsanitize=address -D_GLIBCXX_DEBUG -std=c++11 list-remove.cpp

Я получаю heap-use-after-free:

==31868==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000020 at pc 0x0000004fa1ae bp 0x7fff52cc5630 sp 0x7fff52cc5628
READ of size 4 at 0x603000000020 thread T0
    #0 0x4fa1ad in std::__debug::list<int, std::allocator<int> >::remove(int const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/debug/list:649:18
    #1 0x4f990f in main /tmp/list-remove.cpp:5:7
    #2 0x7ff27d974b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
    #3 0x41b879 in _start (/tmp/list-remove+0x41b879)

Кажется, когда remove находит x, соответствующий первому элементу, он удаляет элемент из списка и удаляет его.Когда он проверяет второй элемент, он использует x, который уже был удален, для сравнения элемента.

Это правильная реализация в соответствии со стандартом C ++?Кажется, для него было бы лучше сначала переместить элементы в конец, а затем удалить их.Это позволит избежать ошибки heap-use-after-free, но, возможно, такая реализация не требуется.

Из cppreference не упоминается, что value не может быть псевдонимом элемента вКонтейнер.

Вот версия C ++, которую я использую:

$ /usr/bin/c++ --version
c++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

1 Ответ

6 голосов
/ 20 сентября 2019

Этот вопрос был задан (и получен ответ) в LWG 526 , в котором сказано:

list::remove(value) требуется для работы, поскольку стандарт не дает на это разрешенияне работать.

Это было исправлено в libc ++ в 2014 году

...