在这种情况下是否可以避免复制lambda仿函数?

sum*_*ght 5 c++ lambda finally c++11

我在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)
}
Run Code Online (Sandbox Code Playgroud)

代码按预期工作,但在Finalizer struct(1)的ctor处有不需要的copy ctor调用(lambda functor ).(值得庆幸的是,RVO可以避免在finally函数(3 - > 4)的return语句中进行复制构造.)

编译器不会消除copy ctor调用(至少在vc10中 - gcc可能会对其进行优化),如果Finalizer struct(2)中的仿函数类型被更改为引用它,则会因为finally调用时的lambda参数而崩溃( 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);
}
Run Code Online (Sandbox Code Playgroud)

没有开销,只有一个printf调用放在范围的末尾.但是......我不喜欢它.:(我试图用宏包装它,但它需要声明两个"名称" - 一个用于lambda对象,另一个用于终结器对象.

我的目标很简单 -

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

有没有解决办法避免这种情况?

Aar*_*aid 4

我认为 lambda 具有移动构造函数?如果是这样,并且如果您只在 内部使用右值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("%d\n", a); }); // (4)
}
Run Code Online (Sandbox Code Playgroud)

应该可以纠正一些更智能的东西,这些东西也可以与左值一起正常工作,这样你的“优化”版本就会编译并在无法移动时进行复制。在这种情况下,我建议您使用类似的方法来确保函子的类型正确,无论参数是否通过或其他方式Functor<std::remove_reference<functor>::type>传递。&&&