4

I have the following variadic generic lambda and overloaded foo() function defined.

template <typename Lambda>
auto bar(Lambda&& lambda) {
    return [lambda = std::forward<Lambda>(lambda)](auto&& ...args) {
        return lambda(std::forward<decltype(args)>(args)...);
    };
}

void foo(std::function<void()>&& cmd, std::function<void()>&& callback) { std::cout << "void" << std::endl; }
void foo(std::function<bool()>&& cmd, std::function<bool()>&& callback) { std::cout << "bool" << std::endl; }

The following 3 foo() calls print "void".

int main()
{
    // 1
    foo(
        bar( []() {} ),
        bar( []() {} )
    );
    // 2
    foo(
        bar( []() { return true; } ),
        bar( []() {} )
    );
    // 3
    foo(
        bar( []() {} ),
        bar( []() { return true;} )
    );
    // 4) compiler error: foo is ambiguous
    // foo(
    //     bar( []() { return false; } ),
    //     bar( []() { return true; } )
    // );
}

Could you please help me to understand why it successfully compiles statements 1-3 but fails to compile statement 4?

gcc 7.5.0

2
  • I'm curious if bar has any purpose. Doesn't seem to make any difference for this example. I guess it would force any functor to have a placeholder return type, but I can't think of what that might do different. Commented Jun 5, 2020 at 22:27
  • Right, but it was not clear to me when asked the question. Commented Jun 8, 2020 at 8:21

1 Answer 1

8

std::function<void()> can store a function with any return type (void or not), its return value is discarded.

Because of that, in (4) both overloads are applicable, and the compiler can't decide which one to use.

In (1), (2), and (3) on the other hand, at least one of the two lambdas returns void, so the bool overload of foo is not applicable.


Possible solutions are:

  • When passing a functor into foo, cast it to a proper specialization of std::function first. (doesn't look too good)

  • Write a custom wrapper/replacement for std::function, with a different SFINAE for the constructor. (takes effort)

  • Make foo a template (use template parameters as types for the callbacks). Then use decltype to determine the return type, and act accordingly. (this is what I would do)

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

Comments

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.