在这种情况下,是否可以避免复制lambda函子

Is it possible to avoid copying lambda functor in this situation?

本文关键字:复制 lambda 函子 可以避免 是否 这种情况下      更新时间:2023-10-16

我在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("%dn", a); }); // (4)
}

代码按预期工作,但在Finalizer结构(1)的ctor处存在不需要的复制ctor调用(lambda函子的)。(值得庆幸的是,RVO避免了finally函数(3->4)中return语句的复制构造。)

编译器不会消除复制构造函数调用(至少在vc10中是这样-gcc可以优化它),如果Finalizer结构(2)中的函子类型被更改为引用,它将崩溃,因为最终调用(4)处的lambda参数是r-value。

当然,代码可以像下面的一样"优化"

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor& func) : func_(func) {}
    ~Finalizer() { func_(); }
private:
    Functor& func_;
};
int main()
{
    int a = 20;
    auto finalizer = [&]{ printf("%dn", a); };
    Finalizer<decltype(finalizer)> fin(finalizer);
}

没有开销,只有printf调用被放置在作用域的末尾。但是我不喜欢它。:(我试图用宏包装它,但它需要声明两个"名称"——一个用于lambda对象,另一个用于finalizer对象。

我的目标很简单-

  1. 应该消除所有可以避免的不必要的性能开销。理想情况下,不应该有函数调用,每个过程都应该内联
  2. 保持简洁的表达作为其效用函数的目的。允许使用宏,但不鼓励使用

对于这种情况,有什么解决方案可以避免吗?

我认为lambdas有move构造函数?如果是这样,如果只在finally中使用右值,则&&forward将移动而不是复制。

#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("%dn", a); }); // (4)
}

应该可以纠正一些更智能的东西,这些东西也可以正确地使用lvalues,这样你的"优化"版本将编译,并在它无法移动时进行复制。在这种情况下,我建议您使用类似Functor<std::remove_reference<functor>::type>的东西来确保Functor的类型正确,无论参数是由&还是&&传递的。

也许接受构造函数的函子参数作为右值引用?

我只允许通过值传递functions。这是通过STL算法完成的。

然后,两种呼叫方式都可以:

  • 这个
    auto finalizer = finally([&]{ printf("%dn", a); }); // this can be dangerous, if passed by reference to finally.
  • 这个
    auto finalizer = [&]{ printf("%dn", a); };
    Finalizer<decltype(finalizer)> fin(finalizer);

这样做的原因是functions不应该很大。我认为STL算法在传递functions时遵循相同的推理。

这是的完整代码

#include <cstdio>
template<typename Functor>
struct Finalizer
{
    Finalizer(Functor func) : func_(func) {} /// pass by value
    ~Finalizer() { func_(); }
private:
    Functor func_; // 
};
template<typename functor>
Finalizer<functor> finally(functor func)  /// pass by value
{
    return Finalizer<functor>(func); 
}
int main()
{
    int a = 20;
    // print the value of a at the escape of the scope
    auto finalizer = finally([&]{ printf("%dn", a); }); 
}