简体   繁体   中英

std::invoke_result doesn't work in a template function with auto return type

I'm trying to use std::invoke_result_t and it fails when called for nested lambda in function with auto return type. Here is a reproducer:

template <typename T, typename... Args>
auto print_ret_type(T &&t, Args &&... args) {
  using ret_type = std::invoke_result_t<T, Args...>;
  std::cout << typeid(ret_type).name() << std::endl;
}

int main(int argc, const char **argv) {
  auto worker = [](int i) {
    auto worker_impl = [](int i, auto &worker) {
      print_ret_type(worker, i, worker);
    };
    worker_impl(i, worker_impl);
  };
  worker(10);

  return 0;
}

I get the following error:

include/c++/9.3.0/type_traits:2783:5: error: no type named 'type' in 'std::invoke_result<(lambda at test.cpp:13:24) &, int &, (lambda at test.cpp:13:24) &>'
    using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;

If I use void return type for print_ret_type instead of auto the test compiles successfully. There is a returned template structure in my original code, which depends on ret_type . Can anyone explain why I get an error when auto return type is used?

The rules for how return type deduction work are a bit quirky.

To determine the return type, it actually instantiates the body of the function. Any errors in this instantiation are hard errors.

Lambdas return types are implicitly ->auto basically; so those rules apply to them. If you want to know the return type of worker , every line of worker must be instantiated , and ditto for worker_impl .

auto worker = [](int i) {
  auto worker_impl = [](int i, auto &worker) {
    print_ret_type(worker, i, worker);
  };
  worker_impl(i, worker_impl); // << worker_impl has no return type yet
};
worker(10);

The return type is demanded in:

template <typename T, typename... Args>
auto print_ret_type(T &&t, Args &&... args) {
  using ret_type = std::invoke_result_t<T, Args...>;

this function is instantiated inside worker_impl to determine its return type in order to determine the return type of worker_impl .

Change it to add ->void to worker_impl :

auto worker_impl = [](int i, auto &worker)->void {

and it compiles, or print_ret_type to return void .

Either one blocks the instantiation of the invoke result at a point where we don't know the return type of worker_impl yet.


Naively, the simple rule "no return statement, an auto return body returns void " would be what you'd think is used. But C++ doesn't have that rule.

Return type deduction does not untangle all possible cases. Sometimes you have to make the return type explicit.

And the rules for what happens when deducing return type are not as narrow as they could be. While the return type of print_ret_type does not matter to the return type of worker_impl , the standard (out of generosity to compiler implementors) gets them to do something closer to "real compilation" there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM