titarch
titarch

Reputation: 123

Why unspecified lambda return types don't always work?

I don't understand why unspecified lambda return types will work sometimes but not all the time. In particular in this piece of code:

template <typename T>
concept NoArgFunc = 
requires (T f) {
    f();
};

auto curry(NoArgFunc auto&& f) {
    return f();    
}

auto curry(auto&& f) {
    return [=](auto&& ...x) {
        return curry([=](auto&& ...xs) -> decltype(f(x..., xs...)) {
                return f(x..., xs...);
        });
    };
}


int main() {
    auto f = [](auto a, auto b, auto c, auto d, auto e) {
        return a * b * c * d * e;
    };

    std::cout << curry(f)(1, 2)()(3)(4, 5);
}

This code compiles and outputs 120 as expected, yet if I don't specify decltype(f(x..., xs...)) for the lambda in the recursive return of the curry function, it does not compiles and complains that I gave 2 arguments instead of 5 which would mean the curryfication did not work. Why is that?

I have tried to search and understand why this return type specification is necessary without success.

Here is the complete error message when the decltype is not provided:

main.cpp: In instantiation of ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> [with auto:14 = {}]’:
main.cpp:31:6:   required from ‘auto curry(auto:11&&) [with auto:11 = curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)>]’
main.cpp:40:21:   required from ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]’
main.cpp:52:31:   required from here
main.cpp:41:25: error: no match for call to ‘(const main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>) (const int&, const int&)’
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~
main.cpp:48:14: note: candidate: ‘template<class auto:15, class auto:16, class auto:17, class auto:18, class auto:19> main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>’
   48 |     auto f = [](auto a, auto b, auto c, auto d, auto e) {
      |              ^
main.cpp:48:14: note:   template argument deduction/substitution failed:
main.cpp:41:25: note:   candidate expects 5 arguments, 2 provided
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~
main.cpp: In instantiation of ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> [with auto:14 = {const int&}]’:
main.cpp:41:25:   recursively required from ‘curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> [with auto:14 = {const int&}]’
main.cpp:41:25:   required from ‘curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]::<lambda(auto:14&& ...)> [with auto:14 = {}]’
main.cpp:31:6:   required from ‘auto curry(auto:11&&) [with auto:11 = curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]::<lambda(auto:14&& ...)>]’
main.cpp:40:21:   required from ‘curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]’
main.cpp:52:36:   required from here
main.cpp:41:25: error: no match for call to ‘(const main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>) (const int&, const int&, const int&)’
main.cpp:48:14: note: candidate: ‘template<class auto:15, class auto:16, class auto:17, class auto:18, class auto:19> main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>’
   48 |     auto f = [](auto a, auto b, auto c, auto d, auto e) {
      |              ^
main.cpp:48:14: note:   template argument deduction/substitution failed:
main.cpp:41:25: note:   candidate expects 5 arguments, 3 provided
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~
➜  experiments g++10 -std=c++20 main.cpp
main.cpp: In instantiation of ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> [with auto:14 = {}]’:
main.cpp:31:6:   required from ‘auto curry(auto:11&&) [with auto:11 = curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)>]’
main.cpp:40:21:   required from ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]’
main.cpp:52:31:   required from here
main.cpp:41:25: error: no match for call to ‘(const main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>) (const int&, const int&)’
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~
main.cpp:48:14: note: candidate: ‘template<class auto:15, class auto:16, class auto:17, class auto:18, class auto:19> main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>’
   48 |     auto f = [](auto a, auto b, auto c, auto d, auto e) {
      |              ^
main.cpp:48:14: note:   template argument deduction/substitution failed:
main.cpp:41:25: note:   candidate expects 5 arguments, 2 provided
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~
main.cpp: In instantiation of ‘curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> [with auto:14 = {const int&}]’:
main.cpp:41:25:   recursively required from ‘curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> [with auto:14 = {const int&}]’
main.cpp:41:25:   required from ‘curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]::<lambda(auto:14&& ...)> [with auto:14 = {}]’
main.cpp:31:6:   required from ‘auto curry(auto:11&&) [with auto:11 = curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]::<lambda(auto:14&& ...)>]’
main.cpp:40:21:   required from ‘curry<curry<curry<main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>&>::<lambda(auto:13&& ...)> [with auto:13 = {int, int}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {}]::<lambda(auto:14&& ...)> >::<lambda(auto:13&& ...)> [with auto:13 = {int}]’
main.cpp:52:36:   required from here
main.cpp:41:25: error: no match for call to ‘(const main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>) (const int&, const int&, const int&)’
main.cpp:48:14: note: candidate: ‘template<class auto:15, class auto:16, class auto:17, class auto:18, class auto:19> main()::<lambda(auto:15, auto:16, auto:17, auto:18, auto:19)>’
   48 |     auto f = [](auto a, auto b, auto c, auto d, auto e) {
      |              ^
main.cpp:48:14: note:   template argument deduction/substitution failed:
main.cpp:41:25: note:   candidate expects 5 arguments, 3 provided
   41 |                 return f(x..., xs...);
      |                        ~^~~~~~~~~~~~~

Upvotes: 3

Views: 104

Answers (1)

Jarod42
Jarod42

Reputation: 217265

decltype serves as SFINAE too.

Else [](auto&& ...xs){/**/} is callable with any parameters, but might produce hard error.

So, is a viable argument to auto curry(NoArgFunc auto&& f).

Upvotes: 2

Related Questions