Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Lambda cannot be implicitly converted to std::function

I am attempting to create a wrapper around an std::function for reasons not explained here. I know that the following code works.

std::function<void()> function = [&]() -> void {};

The following is my wrapper around the std::function, however, it does not work when I try to construct it with a lambda.


template<typename Type, typename ...Args>
class custom_function {
public:
    using function_type = std::function<Type(Args...)>;

    custom_function(const function_type &other) {
        function = other;
    }

private:
    function_type function;
};

// error: conversion from 'main()::<lambda()>' to non-scalar type 'custom_function<void>' requested
custom_function<void> function = [&]() -> void {

};

I thought that this would work since a lambda can be assigned to a std::function. If I add the following constructor, the code now compiles.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

template<typename Type, typename ...Args>
class custom_function {
    // ...

    template<typename Lambda>
    custom_function(const Lambda &other) {
        function = other;
    }

    // ...
};

// this is now valid
custom_function<void> function = [&]() -> void {

};

Why does this constructor work but the previous constructor did not? Why is…

custom_function(const function_type &other) {
    function = other;
}

different from…

template<typename Lambda>
custom_function(const Lambda &other) {
    function = other;
}

I’m compiling with C++17 using G++ on Windows. Thanks

>Solution :

It’s one too many implicit conversion steps. Generally speaking, C will let you get away with one level of indirection. So we could call a function that expects a double and pass it an int, and things will work fine, because there’s an implicit conversion from double to int. Now let’s look at your code.

custom_function(const function_type &other) {
  function = other;
}

This is a converting constructor, which is just a fancy way of saying it’s a constructor that takes one argument of some other type. When we write

custom_function<void> f = [&]() -> void {

};

This is a copy initialization. In principle, it’s constructing something on the right hand side and then assigning it to the left. Since we don’t explicitly call a constructor on the right hand side, we have to convert our lambda into custom_function<void>. We could do that if either

  • There was an conversion operator to custom_function<void> defined on the right hand type (which will never be the case here, since the right hand type is a lambda type), or
  • There was a converting constructor on custom_function<void> that takes a lambda as argument. This won’t work here, since the only constructor takes a std::function. And we’re already talking about a conversion, so we’re not going to consider doing another conversion to get to std::function.

When you replace your constructor with a template function that can take any type, then that template function suffices as a valid conversion from the lambda type directly to your custom function type.

Note that you can also use brace initialization to directly call your constructor. This will work with either constructor.

custom_function<void> f { [&]() -> void {} };

That’s because this is an actual explicit constructor call and therefore will consider implicit conversions to get from the argument type to the declared constructor parameter type.


See also this discussion on the different initialization techniques.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading