Мне кажется, что эта копия присуща дизайну класса, в частности, существует invoke_static_func
.
Из того, что я вижу, это прокси для нормализации статических функций и функций-членовв только функции-члены, так что каждая их отправка может быть выполнена как вызов функции-члена.Единственное отличие состоит в том, что этот элемент является экземпляром fast_delegate_base
, а не экземпляром какого-либо класса, членом которого является целевая функция.
Таким образом, при вызове статических функций существует дополнительный фрейм вызова и избавление отЭта дополнительная копия вам понадобится, чтобы дополнительный кадр вызова (invoke_static_func
) принял свой параметр по ссылке (пока игнорируйте последствия этого, если тип аргумента не является значением).
К сожалению, invoke_static_func
должен вызываться через указатель функции, у которого есть список аргументов, содержащий типы значений, поэтому operator()
вынужден сделать копию для вызова указателя функции (то есть для вызова invoke_static_func
).Заставить invoke_static_func
принимать параметры по ссылке не помогает, потому что он все равно должен вызываться через указатель функции, который не имеет ссылочных типов аргументов.
И нет способа invoke_static_func избежать создания копии для вызоваtest (C1), это просто простой вызов по значению, поэтому вам понадобятся обе копии, чтобы этот проект работал.
Чтобы объяснить это с другой точки зрения, с точки зрения чистого C:
Оператор () должен вызвать функцию func (this_ptr, arg_1, arg_2, arg_3)
.Целевая функция будет ожидать, что эти параметры будут в определенных регистрах или в определенных местах стека в зависимости от их положения в списке аргументов и размера.
Но статическая функция не имеет первого магического параметра «this», его сигнатурыпросто func(arg_1, arg_2, arg_3)
.Таким образом, он ожидает, что все остальные аргументы будут в других регистрах и / или расположениях стека, чем соответствующая функция-член.Поэтому вам нужна эта копия, чтобы переместить аргументы в правильные регистры / ячейки стека, чтобы соответствовать соглашению о вызовах для статической функции.
Что в принципе означает, что вы не можете избежать этой второй копии для статической функции сэтот дизайн.
Однако ... вы можете улучшить это с помощью некоторого хитрого метапрограммирования шаблонов, чтобы применить std :: move к значениям аргументов типа в реализации invoke_static_func
, уменьшая ваш вызовнакладные расходы на копию и перемещение, которое почти равно одной копии.
Я обновлю этот ответ, если и когда выясню, возможно ли 1035 * (и еслину и как).
Редактировать
Примерно так должно получиться:
template <bool IsClass, class U>
struct move_if_class
{
template <typename T>
T&& operator()(const T& t) { return std::move(const_cast<T&>(t)); }
};
template <class T>
struct move_if_class<false,T>
{
T&& operator()(typename std::remove_reference<T>::type& t) { return std::forward<T>(t); }
T&& operator()(typename std::remove_reference<T>::type&& t) { return std::forward<T>(t); }
};
R invoke_static_func(P... args) const
{
return (*(closure_.GetStaticFunction()))(move_if_class<std::is_class<P>::value,P>()(args)...);
}
И после добавления хода c'tor:
C1()
C1(const C1&)
C1(C1&&)
C1::test(1234)
~C1()
~C1()
~C1()