Search code examples
c++lambdamove

selecting a move-constructor in a lambda-captured object


As a test, a class has the copy-constructor defined and the move-constructor explicitly deleted so that an object cannot be move-constructed.

struct foo {
    foo()=default;
    foo(const foo&) { std::cout << "foo copied\n"; }
    foo(foo&&)=delete;
};

foo f;
foo a = f;        // ok
foo b = move(f);  // fails (expected)

It is my understanding that when a move-constructor is explicitly deleted, its declaration is still around for the overload process and that's why foo b cannot be constructed. With only the copy-constructor defined, the move-constructor would not be declared and the copy-constructor const foo& argument would accept the rvalue.

However, when I put the object in a lambda, it compiles:

foo f;
auto lmb = [f]() { }; // f copied into the closure object

auto x = std::move(lmb);

The lambda is cast to an rvalue, but the object it holds is still copied (per the output).
(When the foo move-constructor is defined, it is called, as expected).

Question: why is the (deleted) foo move-constructor not selected (so that it fails to compile) ?


Solution

  • Question: why is the (deleted) foo move-constructor not selected (so that it fails to compile) ?

    Because the lambda's definition is defaulted and not explicitly deleted, therefore overload resolution is tuned to ignore it.

    [over.match.funcs.general]

    8 A defaulted move special member function ([class.copy.ctor], [class.copy.assign]) that is defined as deleted is excluded from the set of candidate functions in all contexts.

    It's hard-coded into the language since C++11 and move semantics being introduced. Otherwise, older code that was written prior to move operations being available (and that would have had the move operations implicitly deleted) would silently break on upgrade. Bad.