5

我在 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 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);
}

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

我的目标很简单——

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

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

4

3 回答 3

4

我认为 lambdas 有移动构造函数?如果是这样,并且如果您只会在 内部使用右值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)
}

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

于 2012-01-18T01:20:05.673 回答
0

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

于 2012-01-18T01:12:01.157 回答
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); }); 
}
于 2019-08-30T15:41:51.440 回答