Why can't I convert a lambda with a non-copyable class parameter to a std::function?

Viewed 146

Consider the code below which fails to compile:

#include <mutex>
#include <functional>

class t{
    std::mutex m;
};

std::function<void(t test)> func = [](t test) {return;};

The following error is generated :

error: conversion from '<lambda(t)>' to non-scalar type 'std::function<void(t)>' requested

Can you explain to me why this conversion is not working?

PS : live example here : https://godbolt.org/z/7se8crf41

1 Answers

The diagnostic is super misleading. While it is obvious that mutex is not copyable, there is no copying of mutex requested in the provided snippet!

The immediate cause of issue is the fact that std::function<> templated constructor (one which would be called when std::function is created from the lambda) is SFINAE-restricted on object being Callable with types of arguments provided.

Callable is not satisfied, since the calling expression tries to pass mutex by value, and mutex is not copyable.

As a result, compiler can't find any suitable constructor to construct the std::function object and issues diagnostic observed.

It also seems the inability to construct an std::function like that is somewhat unfortunate, since you would be able to call a normal function or lambda with temporary-constructed object like

f(t{});

due to guaranteed copy elision. While it makes no sense to do so for std::mutex, it could be meaningful for other non-copyable, non-movable classes.

Related