我最终使用 C++11 中的 lambda 制作了一个模拟器,如下所示:
#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)
}
代码按预期工作,但在 Finalizer 的构造函数中存在不需要的复制构造函数调用(lambda 仿函数)。 em> 结构(1)。 (值得庆幸的是,RVO 避免了 finally 函数 (3 -> 4) 中 return 语句处的复制构造。)
编译器不会消除复制构造函数调用(至少在 vc10 中 - gcc 可以优化)它),并且如果将 Finalizer 结构体 (2) 中的函子类型更改为引用,它将崩溃,因为调用 (4) 中的 lambda 参数为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 调用被放置在作用域的末尾。但是……我不喜欢。 :( 我尝试用宏包装它,但它需要声明两个“名称” - 一个用于 lambda 对象,另一个用于终结器对象。
我的目标很简单 -
- 应该消除所有可以避免的不必要的性能开销。理想情况下,不应该有函数调用,每个过程都应该内联,
- 它的实用函数的目的是允许的,但不鼓励使用宏。
因为
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 -
- Every unnecessary performance overhead which can be avoided should be eliminated. Ideally, there should be no function call, every procedure should be inlined.
- Keep the concise expression as its purpose of utility function. Use of macro is allowed, but discouraged.
Is there any solution to avoid it for this situation?
发布评论
评论(3)
我认为 lambda 具有移动构造函数?如果是这样,并且如果您只在
finally
中使用右值,那么&&
和forward
将移动而不是复制。应该可以纠正一些更智能的东西,这些东西也可以与左值一起正常工作,这样你的“优化”版本就会编译并在无法移动时进行复制。在这种情况下,我建议您使用类似
Functor::type>
的内容来确保 Functor 的类型正确,无论参数是否传递通过&
或&&
或其他。I presume lambdas have move constructors? If so, and if you will only ever use rvalues inside
finally
, then&&
andforward
will move rather than copy.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.也许接受构造函数的函子参数作为右值引用?
Perhaps accept the functor argument to the constructor as an rvalue reference?
我只是允许
函数
按值传递。这是在整个 STL 算法中完成的。
那么,两种调用方式都可以:
这样做的原因是
functions
不应该太大。我认为 STL 算法在传递
函数
时遵循相同的推理。这是完整的代码
I would simply allow the
functions
to be passed by value.This is done all through out the STL algorithms.
Then, both ways of calling would be fine:
The reason for doing so, would be that
functions
should not be large.I think STL algorithms follow the same reasoning when passing
functions
around.Here is the full code