【发布时间】:2012-01-18 01:07:48
【问题描述】:
我在 C++11 中使用 lambda 制作了一个 finally 模拟器,如下所示:
#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 结构 (1) 的 ctor 处存在不希望的复制 ctor 调用(lambda 仿函数)。 (幸运的是,RVO 避免了 finally 函数 (3 -> 4) 中 return 语句的复制构造。)
编译器不会消除复制 ctor 调用(至少在 vc10 中 - gcc 可能会对其进行优化),并且如果 Finalizer 结构 (2) 中的仿函数类型更改为引用它将崩溃,因为 finally 调用 (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 对象,另一个用于终结器对象。
我的目标很简单 -
- 应该消除所有可以避免的不必要的性能开销。理想情况下,不应该有函数调用,每个过程都应该内联。
- 保持简洁的表达式作为其效用函数的目的。允许使用宏,但不鼓励使用。
有什么办法可以避免这种情况吗?
【问题讨论】:
-
至少说
template<typename functor> Finalizer<functor> finally(functor&& func) { return Finalizer<typename std::remove_reference<functor>::type>(std::forward<functor>(func)); }。 -
如果你需要复制 lambda 因为它是类的成员,那么你需要复制它,故事结束。但问题是什么 - 一个 ref-capturing lambda 充其量将包含一些复制成本不高的引用。
-
这就是 C++ 右值引用的用途。
-
感谢您的每一个回答。 (对不起我的反应迟钝-我的英语很短:()我尝试使用 R 值,它不能解决问题。我认为 Kerrek SB 是正确的-问题的根源在于 lambda 对象本身是实例化为临时对象。可能需要在编译时模拟正常顺序评估之类的东西来解决这个问题。(我什至不知道这是否可能)
-
不建议使用引用成员,因为它所绑定的对象的生命周期永远不会延长,并且一旦构造函数运行完毕,引用就会悬空。