I made a finally simulator using lambda in C++11 as below:
#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)
}
The code works as intended, but there is undesired copy ctor call (of lambda functor) at the ctor of Finalizer struct (1). (Thankfully, copy construction at the return statement in the finally function (3 -> 4) is avoided by RVO.)
Compiler does not eliminate the copy ctor call (at least in vc10 - gcc may optimize it), and if the type of the functor in Finalizer struct (2) is changed to reference it'll crash since the lambda argument at the finally call (4) is r-value.
Of course the code can be "optimized" like below
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);
}
No overhead, only a printf call is placed at the end of scope. But... I don't like it. :( I tried to wrap it with macro, but it needs to declare two "name" - one for lambda object, the other for finalizer object.
My objective is simple -
Is there any solution to avoid it for this situation?
I presume lambdas have move constructors? If so, and if you will only ever use rvalues inside finally
, then &&
and forward
will move rather than copy.
#include <cstdio>
template<typename Functor>
struct Finalizer
{
Finalizer(Functor&& func) : func_(std::move(func)) {}
Finalizer(Functor const& func) : func_(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<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)
}
It should be possible to right something more intelligent that will work correctly with lvalues too, so that you're 'optimized' version will compile and will copy when it cannot move. In that case, I suggest you use something like Functor<std::remove_reference<functor>::type>
to be sure that the Functor is of the right type, regardless of whether the parameters were passed around by &
or &&
or whatever.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With