Можно ли избежать копирования лямбда-функтора в этой ситуации? - PullRequest
5 голосов
/ 18 января 2012

Я сделал наконец симулятор с использованием лямбды в C ++ 11, как показано ниже:

#include <cstdio>

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor& func) : func_(func) {} // (1)
    ~Finalizer() { func_(); }

private:
    Functor func_; // (2)
};

template<typename functor>
Finalizer<functor> finally(functor& func)
{
    return Finalizer<functor>(func); (3)
}

int main()
{
    int a = 20;

    // print the value of a at the escape of the scope
    auto finalizer = finally([&]{ printf("%d\n", a); }); // (4)
}

Код работает, как и предполагалось, но есть нежелательный вызов ctor для копирования (лямбда-функтора)) в ctor Финализатор struct (1).(К счастью, RVO избегает создания копии в операторе возврата в функции finally (3 -> 4).)

Компилятор не устраняет вызов копирования ctor (по крайней мере, в vc10- gcc может оптимизировать его), и если тип функтора в Finalizer struct (2) будет изменен, чтобы ссылаться на него, он потерпит крах, поскольку аргумент lambda при вызове finally (4)) - это r-значение.

Конечно, код можно «оптимизировать», как показано ниже

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor& func) : func_(func) {}
    ~Finalizer() { func_(); }

private:
    Functor& func_;
};

int main()
{
    int a = 20;

    auto finalizer = [&]{ printf("%d\n", a); };
    Finalizer<decltype(finalizer)> fin(finalizer);
}

Никаких накладных расходов, только вызов printf выполняется в концеобласти видимостиНо ... мне это не нравится.:( Я пытался обернуть его макросом, но ему нужно объявить два «имени» - одно для лямбда-объекта, другое для объекта финализатора.

Моя цель проста -

  1. Все ненужные издержки производительности, которых можно избежать, должны быть исключены. В идеале не должно быть вызова функции, каждая процедура должна быть встроенной.
  2. Сохранять краткое выражение в качестве цели вспомогательной функции. Использование макроса разрешено, но обескуражен.

Есть ли какое-нибудь решение, чтобы избежать этого в этой ситуации?

Ответы [ 2 ]

4 голосов
/ 18 января 2012

Я предполагаю, что у лямбд есть конструкторы перемещения?Если это так, и , если вы когда-либо будете использовать только значения r внутри finally, тогда && и forward будут перемещаться, а не копироваться.

#include <cstdio>

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor&& func) : func_(std::forward(func)) {} // (1)
    ~Finalizer() { func_(); }

private:
    Functor func_; // (2)
};

template<typename functor>
Finalizer<std::remove_reference<functor>::type> finally(functor&& func)
{
    return Finalizer<std::remove_reference<functor>::type>(std::forward(func)); // (3)
}

int main()
{
    int a = 20;

    // print the value of a at the escape of the scope
    auto finalizer = finally([&]{ printf("%d\n", a); }); // (4)
}

Должно быть возможноПравильно, что-то более интеллектуальное, что будет правильно работать и с lvalues, так что ваша «оптимизированная» версия будет скомпилирована и скопирована, когда она не сможет двигаться.В этом случае я предлагаю вам использовать что-то вроде Functor<std::remove_reference<functor>::type>, чтобы быть уверенным, что Functor относится к нужному типу, независимо от того, были ли параметры переданы & или && или чем-то еще.

0 голосов
/ 18 января 2012

Возможно, принять аргумент functor для конструктора как ссылку на значение?

...