Segfault в минимальном примере scoped_allocator_adaptor, используемого с вектором unordered_map - PullRequest
0 голосов
/ 06 июня 2018

Я недавно узнал о пользовательских распределителях в C ++ 11 и пытаюсь использовать их в своем приложении.Однако я сталкиваюсь с segfault, который может быть воспроизведен следующим минимальным примером:

#include <vector>
#include <unordered_map>
#include <scoped_allocator>

template<class T> struct custom_allocator {
    typedef T value_type;
    custom_allocator() noexcept {}
    template <class U> custom_allocator (const custom_allocator<U>&) noexcept {}
    T* allocate (std::size_t n) { return static_cast<T*>(::operator new(n*sizeof(T))); }
    void deallocate (T* p, std::size_t n) { ::delete(p); }
    template <typename U> constexpr bool operator==(const custom_allocator<U>&) const { return true; }
    template <typename U> constexpr bool operator!=(const custom_allocator<U>&) const { return false; }
};

template<class T> using custom_scoped_allocator = std::scoped_allocator_adaptor<custom_allocator<T> >;

typedef std::unordered_map<int, int, std::hash<int>, std::equal_to<int>,
                           custom_scoped_allocator<std::pair<const int, int> > > MyMap;

typedef std::vector<MyMap, custom_scoped_allocator<MyMap> > MyVector;

int main() {
    MyVector vec(1);
    vec[0][0] = 0;
    return 0;
}

Пользовательский распределитель - тот, который предложен в примере в http://www.cplusplus.com/reference/memory/allocator_traits/. Затем он комбинируется сstd::scoped_allocator_adaptor, как я видел в нескольких местах в Интернете.

Код прекрасно компилируется с gcc 5.4.0 с использованием g++ -g3 -O0 -std=c++11 -march=core-avx-i -o tmpalloc tmpalloc.cpp.Когда я пытаюсь запустить его, появляется сообщение об ошибке, начиная с

*** Error in `./tmpalloc': double free or corruption (fasttop): 0x0000000000ae3c90 ***

. Если я перекомпилирую с помощью AddressSanitizer, я получу следующие сведения о двойном освобождении: https://pastebin.com/raw/xy2NQtD0 (извините за вставку, ноэто не красиво.) По сути, деструктор для объекта unordered_map, кажется, вызывается дважды.

Что я делаю не так?

1 Ответ

0 голосов
/ 06 июня 2018

Ваша allocate функция выделяет группу байтов, но не вызывает никаких конструкторов (путем непосредственного вызова глобальной функции operator new).Ваша функция deallocate вызывает delete для типизированного указателя, что приводит к тому, что деструктор для этого типа вызывается до вызова глобальной функции operator delete для освобождения памяти.Конечным результатом является то, что вы уничтожаете память, которую вы не создали (создание и уничтожение обрабатываются вызывающей стороной после / до вызова ваших allocate / deallocate функций.

Вы должны освободить памятьиспользование дополнительного метода к тому, как вы его выделяете. В C ++ 14 или более поздних версиях это будет:

void deallocate (T* p, std::size_t n) { ::operator delete(p, n * sizeof(T)); }

, где n - значение, переданное allocate, а второй параметр - этозначение передается в new. До C ++ 14 просто ретранслируйте версию с одним параметром (что и будет делать оператор удаления по умолчанию с двумя параметрами):

void deallocate (T* p, std::size_t n) { ::operator delete(p); }
...