Я пишу пользовательский распределитель памяти. Если возможно, я хочу сделать функцию создания объекта подобной этой, чтобы полностью абстрагировать процедуру создания.
template<typename T>
class CustomCreator
{
virtual T& createObject(T value) __attribute__((always_inline))
{
T* ptr = (T*)customAlloc();
new (ptr) T(value);
return *ptr;
}
}
Но это вызывает копирование. Есть ли способ заставить исключить копирование в этом случае?
Вот мой текущий код тестирования.
#include <iostream>
struct AA
{
inline static void test(AA aa) __attribute__((always_inline))
{
AA* ptr = new AA(aa);
delete ptr;
}
AA(AA const& aa)
{
printf("COPY!\n");
}
AA(int v)
{
printf("CTOR!\n");
}
};
int main(int argc, const char * argv[])
{
AA::test(AA(77));
return 0;
}
Я пытался передать value
как T&
, T const&
, T&&
, T const&&
, но он все еще копирует.
Я ожидал, что оптимизатор исключит функциональный кадр, поэтому параметр функции может быть выведен в R-значение, но я все еще вижу COPY!
сообщение.
Я также попробовал шаблон пересылки C ++ 11, но не смог его использовать, потому что он не может быть виртуальной функцией.
Мой компилятор Clang включен в Xcode. (clang-425.0.28) И уровень оптимизации установлен на -Os -flto
,
Я написал дополнительные тесты и проверил сгенерированный LLVM IR clang -Os -flto -S -emit-llvm -std=c++11 -stdlib=libc++ main.cpp;
опции. Что я мог наблюдать: (1) функциональные кадры всегда могут быть устранены. (2) если большой объект (4096 байт) передается по значению, он не удаляется. (3) Передача эталонного значения r с использованием std::move
работает хорошо. (4) если я не создаю move-constructor, компилятор в основном прибегает к copy-constructor.
Первый:
Вы ожидали, что оптимизатор исключит функциональный фрейм, но не может, потому что (1) эта функция не является допустимым случаем для RVO, чтобы полностью обмануть и пропустить конструктор копирования, и (2) Ваша функция имеет побочные эффекты. А именно написание copy
на экран. Таким образом, оптимизатор не может оптимизировать копию, потому что код говорит, что должен написать copy
дважды. Что бы я сделал, это удалить printf
позвоните, и проверьте сборку. Скорее всего, если это просто, это является быть оптимизированным.
Во-вторых:
Если члены-конструкторы копирования могут иметь побочные эффекты (например, выделение памяти), то, скорее всего, вы правы, что это не оптимизируется. Тем не менее, вы можете сказать это переехать членов, а не копировать их, что, вероятно, то, что вы ожидали. Чтобы это работало, вам нужно убедиться, что в вашем классе есть конструктор перемещения вместе с конструктором копирования.
inline static void test(AA aa) __attribute__((always_inline))
{
AA* ptr = new AA(std::move(aa));
delete ptr;
}
В третьих:
На самом деле, даже это не то, что вы хотите. То, что вы, вероятно, хотите, это что-то вроде идеальной пересылки, которая будет просто передавать параметры конструктору без копирования НИЧЕГО. Это означает, что даже если оптимизатор полностью отключен, ваш код по-прежнему избегает копий. (Обратите внимание, что это может не относиться к вашей ситуации, шаблоны не могут быть virtual
вот так, если только это не специализировано)
template<class ...types>
inline static void test(types&&... values) __attribute__((always_inline))
{
AA* ptr = new AA(std::forward<types>(values)...);
delete ptr;
}
Разве ваш экземпляр-конструктор не вызывается явно AA* ptr = new AA(aa);
? Если вы хотите избежать копирования значения инициализации, вы должны сделать это так:
#include <iostream>
#include <utility>
int main(int argc, const char * argv[])
{
struct
AA
{ inline static void test(AA aa) __attribute__((always_inline))
{
AA* ptr = new AA(std::move(aa));
delete ptr;
}
inline static void test(AA&& aa) __attribute__((always_inline))
{
AA* ptr = new AA(std::move(aa));
delete ptr;
}
AA(AA const& aa)
{
printf("COPY!\n");
}
AA(AA const&& aa) // Move constructors are not default here - you have to declare one
{
printf("MOVE!\n");
}
AA(int v)
{
printf("CTOR!\n");
xx = v;
}
int xx = 55;
};
AA::test(AA(77));
return 0;
}
Проблемы:
Во-первых, давайте решим ваши A&&
переместить проблему. Вы можете получить конструктор перемещения, сделав конструктор перемещения (или объявив его по умолчанию) (или позволив компилятору его сгенерировать), и убедившись, что вы вызываете std::move
на aa
когда вы проходите через: Живой пример здесь.
То же самое относится к любой виртуальной функции экземпляра, с которой вы работаете. Я также включил версию перенаправления variadic в пример, на всякий случай, если вы хотите получить справку о том, как это сделать (обратите внимание, все аргументы std::forward
к их следующим звонкам).
В любом случае, это похоже на вопрос, возникший из серьезного случая XY
то есть: вы пытаетесь решить проблему, которую создали с помощью своего текущего дизайна. Я не знаю, почему вам нужно создавать объекты с помощью методов экземпляра на уже существующем объекте (создание типа фабрики?), Но это звучит грязно. В любом случае, вышесказанное должно помочь вам достаточно, чтобы начать работу. Просто убедитесь, что если вы перемещаете параметры, чтобы всегда обернуть &&
предмет с std::move
(или же std::forward
если вы работаете с универсальными ссылками и параметрами шаблона).