且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使C ++ lambda对象的存储更有效?

更新时间:2023-11-29 16:04:46


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


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



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


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


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

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

否。这不是你的编译器的错误;这就是 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.