2012-01-18 2 views
3

Я сделал наконец симулятор с помощью лямбда в 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 из Finalizer struct (1). (К счастью, скопировать конструкцию на оператора возврата в функции наконец (3 -.> 4) избегают РВО)

Компилятор не исключает вызов копирования CTOR (по крайней мере, в VC10 - НКУ может оптимизировать его) , и если тип функтора в Finalizer struct (2) заменен на ссылку, он будет сбой, так как аргумент лямбда на , наконец, call (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. Держите краткое выражение в качестве своей цели функции полезности. Использование макрокоманды допускается, но не рекомендуется.

Есть ли солю чтобы избежать этого для этой ситуации?

+1

По крайней мере, сказать 'шаблон <имяТипа функтор> Финалайзер наконец (функтор && FUNC) {вернуться Finalizer <имяТипа станд :: remove_reference :: тип> (станд :: вперед (FUNC)); } '. –

+0

Если вам нужно скопировать лямбду, потому что это член класса, то вам нужно скопировать его, конец истории. Но в чем проблема - в лучшем случае ref-capture lambda будет содержать несколько ссылок, которые не так дорого копировать. –

+0

Это то, на что были сделаны ссылки на C++ rvalue. –

ответ

3

Предполагаю, что у lambdas есть механизмы перемещения? Если да, то и, если вы только когда-либо используете rvalues ​​внутри 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

Этот мой ответ все еще немного сломан. Я сделаю это community-wiki. Это слишком поздно для меня, чтобы написать правильный код :-) –

+1

У этого решения есть проблема, что два функтора могут быть вызваны, если elision не состоится: один раз, когда деструктор временного, созданный вызовом 'finally '(в конце инструкции) запускается и один раз в конце области, когда' finalizer' уничтожается. Эта проблема также присутствует в вопросе (версия без ссылки). –

+0

@LucDanton, хорошая точка. В то время я не думал об этом. Временное, которое передано * на * 'finally', является типом функтора, оно не является типом« Finalizer ». Поэтому попытки его выполнить не будут. Нам все равно, как многие функторы созданы и уничтожены. Мы заботимся только о том, сколько объектов Finalizer были созданы и уничтожены - вот в чем проблема. Временное выражение, возвращаемое из 'finally', имеет тип' Finalizer'. Если нам повезет, компилятор будет оптимизировать и использовать RVO и т. Д., Чтобы гарантировать, что будет создан только один Finalizer. 'auto_ptr ' может быть? –

0

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

Смежные вопросы