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

发布于 2024-12-28 01:44:24 字数 1427 浏览 0 评论 0 原文

我最终使用 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("%d\n", a); }); // (4)
}

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

编译器不会消除复制构造函数调用(至少在 vc10 中 - gcc 可以优化)它),并且如果将 Finalizer 结构体 (2) 中的函子类型更改为引用,它将崩溃,因为调用 (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. 它的实用函数的目的是允许的,但不鼓励使用宏。

因为

I made a finally simulator using lambda in C++11 as below:

#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)
}

The code works as intended, but there is undesired copy ctor call (of lambda functor) at the ctor of Finalizer struct (1). (Thankfully, copy construction at the return statement in the finally function (3 -> 4) is avoided by RVO.)

Compiler does not eliminate the copy ctor call (at least in vc10 - gcc may optimize it), and if the type of the functor in Finalizer struct (2) is changed to reference it'll crash since the lambda argument at the finally call (4) is r-value.

Of course the code can be "optimized" like below

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

No overhead, only a printf call is placed at the end of scope. But... I don't like it. :( I tried to wrap it with macro, but it needs to declare two "name" - one for lambda object, the other for finalizer object.

My objective is simple -

  1. Every unnecessary performance overhead which can be avoided should be eliminated. Ideally, there should be no function call, every procedure should be inlined.
  2. Keep the concise expression as its purpose of utility function. Use of macro is allowed, but discouraged.

Is there any solution to avoid it for this situation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不及他 2025-01-04 01:44:24

我认为 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)
}

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

I presume lambdas have move constructors? If so, and if you will only ever use rvalues inside finally, then && and forward will move rather than copy.

#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)
}

It should be possible to right something more intelligent that will work correctly with lvalues too, so that you're 'optimized' version will compile and will copy when it cannot move. In that case, I suggest you use something like Functor<std::remove_reference<functor>::type> to be sure that the Functor is of the right type, regardless of whether the parameters were passed around by & or && or whatever.

夜清冷一曲。 2025-01-04 01:44:24

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

Perhaps accept the functor argument to the constructor as an rvalue reference?

黯然 2025-01-04 01:44:24

我只是允许函数按值传递。
这是在整个 STL 算法中完成的。

那么,两种调用方式都可以:

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

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

这是完整的代码

#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); }); 
}

I would simply allow the functions to be passed by value.
This is done all through out the STL algorithms.

Then, both ways of calling would be fine:

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

The reason for doing so, would be that functions should not be large.
I think STL algorithms follow the same reasoning when passing functions around.

Here is the full code

#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); }); 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文