Исключение копирования параметра функции

Я пишу пользовательский распределитель памяти. Если возможно, я хочу сделать функцию создания объекта подобной этой, чтобы полностью абстрагировать процедуру создания.

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.

2

Решение

Первый:
Вы ожидали, что оптимизатор исключит функциональный фрейм, но не может, потому что (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;
}
2

Другие решения

Разве ваш экземпляр-конструктор не вызывается явно 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;
}

Проблемы:

  • Условия, позволяющие конструктор перемещения по умолчанию довольно туго, вам лучше написать самому.
  • Если вы передаете значение (даже временный) вам все еще нужен std :: move to, чтобы передать его еще дальше, не создавая копию.
2

Во-первых, давайте решим ваши A&& переместить проблему. Вы можете получить конструктор перемещения, сделав конструктор перемещения (или объявив его по умолчанию) (или позволив компилятору его сгенерировать), и убедившись, что вы вызываете std::move на aa когда вы проходите через: Живой пример здесь.

То же самое относится к любой виртуальной функции экземпляра, с которой вы работаете. Я также включил версию перенаправления variadic в пример, на всякий случай, если вы хотите получить справку о том, как это сделать (обратите внимание, все аргументы std::forwardк их следующим звонкам).

В любом случае, это похоже на вопрос, возникший из серьезного случая XYто есть: вы пытаетесь решить проблему, которую создали с помощью своего текущего дизайна. Я не знаю, почему вам нужно создавать объекты с помощью методов экземпляра на уже существующем объекте (создание типа фабрики?), Но это звучит грязно. В любом случае, вышесказанное должно помочь вам достаточно, чтобы начать работу. Просто убедитесь, что если вы перемещаете параметры, чтобы всегда обернуть && предмет с std::move (или же std::forward если вы работаете с универсальными ссылками и параметрами шаблона).

0
По вопросам рекламы [email protected]