如何使C ++ lambda对象的存储更有效? [英] How can I make the storage of C++ lambda objects more efficient?

查看:156
本文介绍了如何使C ++ lambda对象的存储更有效?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在考虑存储C ++ lambda的最近。你在互联网上看到的标准建议是将lambda存储在std :: function对象中。然而,这些建议都没有考虑存储的影响。在我看来,必须有一些严重的黑色巫毒在幕后进行,使这项工作。考虑下面存储整数值的类:

  class Simple {
public:
Simple value){puts(Constructing simple!); this-> value = value; }
Simple(const Simple& rhs){puts(Copying simple! this-> value = rhs.value; }
简单(简单&& rhs){puts(移动简单! this-> value = rhs.value; }
〜Simple(){puts(Destroying simple! }
int Get()const {return this-> value; }

private:
int value;
};

现在,考虑这个简单的程序:

  int main()
{
简单测试(5);

std :: function< int()> f =
[test]()
{
return test.Get();
};

printf(%d\\\
,f());
}

这是我希望从这个程序看到的输出:

 构造简单! 
复制简单!
移动简单!
破坏简单!
5
破坏简单!
破坏简单!

首先,我们创建值test。我们在堆栈上为临时lambda对象创建一个本地副本。然后我们将临时lambda对象移动到由std :: function分配的内存中。我们销毁临时lambda。我们打印输出。我们销毁std ::函数。最后,我们销毁测试对象。<​​/ p>

不用说,这是不是我看到的。当我在Visual C ++ 2010(发布或调试模式)编译时,我得到这个输出:

 构造简单! 
复制简单!
复制简单!
复制简单!
复制简单!
破坏简单!
破坏简单!
破坏简单!
5
破坏简单!
破坏简单!

圣诞老人效率低下!不仅编译器不能使用我的移动构造函数,而且在赋值过程中生成并销毁了两个很明显的lambda的副本。



因此,最后是问题:(1)这些复制真的有必要吗? (2)有没有办法强迫编译器生成更好的代码?感谢您阅读!

解决方案


在互联网上看到的标准建议是将lambda存储一个std :: function对象。但是,这些建议都没有考虑存储的影响。


这是因为没关系。您无权访问lambda的typename。所以,虽然你可以存储在它的本机类型与 auto 最初,它不会离开那个范围的类型。您不能将其作为该类型返回。你可以粘在别的东西。而且C ++ 11提供的唯一的别的东西是 std :: function



选择:暂时保持它与 auto ,锁定在该范围内。或者将它放在 std :: function 中以进行长期存储。


是否所有这些复制真的有必要?


技术上?不, std :: function 不需要

$ < b $ b

有没有办法强制编译器生成更好的代码?


否。这不是你的编译器的错误;这就是 std :: function 的具体实现如何工作。它可以减少复制;它不应该复制两次以上(并且取决于编译器如何生成lambda,可能只有一次)。但它确实如此。


I've been thinking about storing C++ lambda's lately. The standard advice you see on the Internet is to store the lambda in a std::function object. However, none of this advice ever considers the storage implications. It occurred to me that there must be some seriously black voodoo going on behind the scenes to make this work. Consider the following class that stores an integer value:

class Simple {
public:
    Simple( int value ) { puts( "Constructing simple!" ); this->value = value; }
    Simple( const Simple& rhs ) { puts( "Copying simple!" ); this->value = rhs.value; }
    Simple( Simple&& rhs ) { puts( "Moving simple!" ); this->value = rhs.value; }
    ~Simple() { puts( "Destroying simple!" ); }
    int Get() const { return this->value; }

private:
    int value;
};

Now, consider this simple program:

int main()
{
    Simple test( 5 );

    std::function<int ()> f =
        [test] ()
        {
            return test.Get();
        };

    printf( "%d\n", f() );
}

This is the output I would hope to see from this program:

Constructing simple!
Copying simple!
Moving simple!
Destroying simple!
5
Destroying simple!
Destroying simple!

First, we create the value test. We create a local copy on the stack for the temporary lambda object. We then move the temporary lambda object into memory allocated by std::function. We destroy the temporary lambda. We print our output. We destroy the std::function. And finally, we destroy the test object.

Needless to say, this is not what I see. When I compile this on Visual C++ 2010 (release or debug mode), I get this output:

Constructing simple!
Copying simple!
Copying simple!
Copying simple!
Copying simple!
Destroying simple!
Destroying simple!
Destroying simple!
5
Destroying simple!
Destroying simple!

Holy crap that's inefficient! Not only did the compiler fail to use my move constructor, but it generated and destroyed two apparently superfluous copies of the lambda during the assignment.

So, here finally are the questions: (1) Is all this copying really necessary? (2) Is there some way to coerce the compiler into generating better code? Thanks for reading!

解决方案

The standard advice you see on the Internet is to store the lambda in a std::function object. However, none of this advice ever considers the storage implications.

That's because it doesn't matter. You do not have access to the typename of the lambda. So while you can store it in its native type with auto initially, it's not leaving that scope with that type. You can't return it as that type. You can only stick it in something else. And the only "something else" C++11 provides is std::function.

So you have a choice: temporarily hold on to it with auto, locked within that scope. Or stick it in a std::function for long-term storage.

Is all this copying really necessary?

Technically? No, it is not necessary for what std::function does.

Is there some way to coerce the compiler into generating better code?

No. This isn't your compiler's fault; that's just how this particular implementation of std::function works. It could do less copying; it shouldn't have to copy more than twice (and depending on how the compiler generates the lambda, probably only once). But it does.

这篇关于如何使C ++ lambda对象的存储更有效?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆