Пользовательский std::allocator для классов с замененным оператором new

Недавно я заменил некоторые классы Vector/Matrix на классы, использующие SSE, и теперь проверяю правильность выравнивания памяти.

Следуя совету в ответе на этот вопрос, я заменил оператор new/delete для классы, которым это требуется, и начали работу над настраиваемым распределителем для использования с контейнерами STL, однако, похоже, между ними существует некоторый конфликт:

Для начала я просто скопировал и вставил пример класса распределителя из здесь, который отлично компилируется, когда я использую его с std::vector рассматриваемых типов без моего собственного нового/удаления, но когда я заменяю эти функции, я получаю сообщение об ошибке "нет функция сопоставления для вызова 'operator new'" из функции построения(),

void    construct(pointer p, const T& t)    { new(p) T(t); }

Я предполагаю, что тот факт, что я заменил "обычный" новый, как-то затуманил размещение нового? Однако, учитывая, что я не могу написать свое собственное размещение нового, чтобы оно могло подобраться, я не совсем уверен, что делать... быть высоко оценен!

Я компилирую в Linux, используя Clang v3.4 (или gcc 4.1.2); не используя С++ 11.

Большое спасибо.


person YamLady    schedule 18.09.2014    source источник


Ответы (3)


Канонический allocator::construct вызывает ::new((void *)p) T(val)

Опуская ::, вы позволяете поиску имени начинаться с области класса T, где он нашел вашу область класса operator new и не продолжал дальше (поиск имен останавливается на первой области, в которой найдены любые совпадающие имена, даже если в какой-то окружающей области существует лучший кандидат)

(приведение к void происходит в случае, если пользователь пробирается в новую глобальную перегрузку с почти размещением, которая принимает непустой параметр указателя)

PS: как правильно указано в комментариях, «учитывая, что я не могу написать свое новое размещение» - неверное предположение. Вы не можете заменить глобальное размещение-new, но вы, безусловно, можете написать новое размещение для класса, которое затем будет выбрано при поиске в области класса. Просмотрите cppreference, чтобы получить сводку о функциях распределения.

person Cubbi    schedule 18.09.2014
comment
Чтобы исправить это, ::new или реализуйте переопределенное размещение new для своего класса. - person Yakk - Adam Nevraumont; 18.09.2014
comment
Ах, да, это имеет смысл... Спасибо! По какой-то причине я полностью пропустил примечание о ccpreference, в котором это указано: несмотря на то, что размещение new (перегрузки 5 и 6) не может быть заменено, функция с той же сигнатурой может быть определена в области класса, как описано выше... Упс. - person YamLady; 19.09.2014

Я бы предложил использовать Boost aligned_allocator:

#include <boost/align/aligned_allocator.hpp>
#include <immintrin.h>
#include <vector>

struct m128i {
    // FIXME: ctors/opers with intrinsics would be nice (required?)
    __m128i data;
}

int main()
{
    std::vector<m128i, boost::alignment::aligned_allocator<m128i, 16> > v;
    v.emplace_back();
}

Примечание. Я обновил это, чтобы использовать структуру, обертывающую встроенный элемент. Есть много библиотек, которые делают это. Причина для этого проста: атрибут vector_size — это то, что отличает __m128i от __m256i от __m512i, а шаблоны игнорируют атрибуты типа, поэтому я полагаю, что все они в конечном итоге будут использовать одно и то же расширение, которое является «длинным длинным» (или плавающим/двойным в случае типов не-i и d).

person James Cape    schedule 04.11.2016

Я бы использовал свою начальную точку в качестве распределителя, указанную здесь: http://en.cppreference.com/w/cpp/concept/Allocator. Что на самом деле является минимальным распределителем. В частности, вам вообще не нужно писать construct. allocator_traits, если он не обнаружит, что ваш аллокатор имеет метод construct, просто вызовет новое для вас размещение, правильно определяя область вызова с помощью :: (как отмечает Cubbi), чтобы у вас не было этой проблемы: http://en.cppreference.com/w/cpp/memory/allocator_traits/construct.

Я, вероятно, вообще не стал бы писать пользовательские новые/удаленные. Достаточно просто написать собственный распределитель, и ваши классы Vector/Matrix будут управлять своими данными через std::vector, используя ваш собственный распределитель.

person Nir Friedman    schedule 04.11.2016