Почему неопределенные лямбда-возвращаемые типы не всегда работают? - PullRequest
3 голосов
/ 06 мая 2020

Я не понимаю, почему неопределенные лямбда-возвращаемые типы иногда работают, но не всегда. В частности, в этом фрагменте кода:

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);
}

Этот код компилирует и выводит 120, как ожидалось, но если я не укажу decltype(f(x..., xs...)) для лямбда в рекурсивном возврате curry функция, она не компилируется и жалуется, что я дал 2 аргумента вместо 5, что означает, что каррификация не работает. Почему?

Я безуспешно пытался найти и понять, почему эта спецификация типа возвращаемого значения необходима.

Вот полное сообщение об ошибке, когда тип decltype не указан:

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...);
      |                        ~^~~~~~~~~~~~~

1 Ответ

2 голосов
/ 06 мая 2020

decltype также служит как SFINAE.

Остальное [](auto&& ...xs){/**/} может быть вызвано с любыми параметрами, но может вызвать серьезную ошибку.

Итак, это жизнеспособный аргумент для auto curry(NoArgFunc auto&& f).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...