0

I've bumped into something strange with C++ copy and move constructors, here when passing to the lambda expression both the copy and move constructors get executed. Strangely though, when I change the declared type of the lambda to auto or use the regular_test function, I get the expected behaviour (copy construction only). Does anyone understand why this is? (tested with both clang and gcc, not msvc)

#include <iostream>
#include <iomanip>
#include <functional>

using namespace std;

struct Test {
    inline Test() {
        cout << setw(20) << "constructor ";
        PrintAddress();
    }

    Test(const Test&) {
        cout << setw(20) << "copy constructor ";
        PrintAddress();
    }

    Test& operator=(const Test&) {
        cout << setw(20) << "copy assignment ";
        PrintAddress();
        return *this;
    }

    Test(Test&& other) {
        cout << setw(20) << "move constructor ";
        PrintAddress();
    }

    Test& operator=(Test&&) {
        cout << setw(20) << "move assignment ";
        PrintAddress();
        return *this;
    }

    virtual ~Test() {
        cout << setw(20) << "destructor ";
        PrintAddress();
    }

    void PrintAddress() {
        cout << "Test&: " << this << endl;
    }
};

Test regular_test (Test t) {
    cout << "regular_test" << endl;
    return t;
}

int main() {
    cout << "start" << endl;
    function<Test(Test)> lambda_test = [] (Test t) {
        cout << "lambda_test" << endl;
        return t;
    };
    Test t;
    lambda_test(t);
    //regular_test(t);
    cout << "done" << endl;
    return 0;
}
start
        constructor Test&: 0x7fffef6faf28
   copy constructor Test&: 0x7fffef6faf08
   move constructor Test&: 0x7fffef6fade8
lambda_test
   move constructor Test&: 0x7fffef6faf10
         destructor Test&: 0x7fffef6fade8
         destructor Test&: 0x7fffef6faf10
         destructor Test&: 0x7fffef6faf08
done
         destructor Test&: 0x7fffef6faf28
1
  • auto: function<Test(Test&)> Commented Sep 3, 2022 at 13:10

1 Answer 1

1

because std::function::operator() is defined based on the template type of class.

R operator()( Args... args ) const; // Args are parameter of the *class*

so std::function<Test(Test)> would have Test operator()(Test), which would make a copy by itself, then forward to lambda_test (a move)

Sign up to request clarification or add additional context in comments.

2 Comments

ok but why does changing function<Test(Test)> lambda_test to auto lambda_test fix the problem?
@DavidCarpenter because auto doesn't add another wrapper. (the lambda is not even copied)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.