Reputation: 123
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
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