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

时间:2012-01-18 01:07:49

标签: c++ lambda c++11 finally

我在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 struct(1)的ctor处有不受欢迎的复制ctor调用(lambda functor)。 (值得庆幸的是,RVO避免在最终函数(3 - &gt; 4)的return语句中进行复制构造。)

编译器不会消除copy ctor调用(至少在vc10中 - gcc可能会优化它),如果 Finalizer struct(2)中的functor类型被更改为引用它崩溃,因为最后调用(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对象,另一个用于终结器对象。

我的目标很简单 -

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

3 个答案:

答案 0 :(得分:4)

我认为lambdas有移动构造函数?如果是这样,,如果您只在finally内使用rvalues,那么&&forward将移动而不是复制。

#include <cstdio>

template<typename Functor>
struct Finalizer
{
    Finalizer(Functor&& func) : func_(std::forward(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(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)
}

应该有可能找到一些更智能的东西,它也可以正确地与左值一起使用,这样你''优化'版本将编译并在它无法移动时复制。在这种情况下,我建议您使用类似Functor<std::remove_reference<functor>::type>的内容来确保Functor的类型正确,无论参数是由&还是&&传递还是其他任何内容。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

然后,两种调用方式都可以:

  • 这个
    auto finalizer = finally([&]{ printf("%d\n", a); }); // this can be dangerous, if passed by reference to finally.
  • 还有这个
    auto finalizer = [&]{ printf("%d\n", 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("%d\n", a); }); 
}