Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

moving std::function into another std::function does not invoke move constructor on captured variables

I have a class A that prints out a message when constructed/copied/moved

class A
{
public:
    A(std::string s)
        :s_(s)
    {
        std::cout << "A constructed\n";
    }
    ~A()
    {
        std::cout << "A destructed\n";
    }
    A(const A& a)
        :s_(a.s_)
    {
        std::cout << "A copy constructed\n";
    }
    A(A&& a)
       :s_(std::move(a.s_))
    {
        std::cout << "A moved\n";
    }
    A& operator=(const A& a)
    {
        s_ = a.s_;
        std::cout << "A copy assigned\n";
    }
    A& operator=(A&& a)
    {
        s_ = std::move(a.s_);
        std::cout << "A move assigned\n";
    }

    std::string s_;
};

In main, I construct an instance of A, capture that in a lambda by value, copy that lambda into a std::function, and finally move that std::function into another std::function

int main()
{
    A a("hello ");
    std::function<void()> f = [a]{ std::cout << a.s_; };
    std::function<void()> g(std::move(f));
}

This prints out the following

A constructed
A copy constructed
A copy constructed
A destructed
A destructed
A destructed

Why is the move constructor of A not invoked? Shouldn't the last step of moving f into g have invoked A's move constructor?

like image 892
tcb Avatar asked Nov 19 '14 16:11

tcb


1 Answers

The copy constructor isn't called precisely because you have moved the std::function. This is because std::function can optionally store the captured values on the heap and retain a pointer to them. Thus moving the function simply requires moving that internal pointer. Obviously MSVC opts to store the captures on the heap and GCC etc. opt to store them on the stack thus requiring the captured values to be moved as well.

Edit: Thanks to Mooing Duck for pointing out in a comment on the question that GCC is also storing the captures on the heap. The actual difference seems to be that GCC moves the captures from the lambda to the std::function when it is constructed from the lambda.

like image 83
sjdowling Avatar answered Oct 19 '22 00:10

sjdowling