Home > Net >  Why does std::apply fail with function template, but not with a lambda expression with explicit temp
Why does std::apply fail with function template, but not with a lambda expression with explicit temp

Time:10-24

In looking at std::apply references from cpprefrence I noticed that function templates cannot be passed as callable object of std::apply. Let's consider the following function template:

template<typename T>
T add_generic(T first, T second) { return first   second; }

we can't call:

std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n'; // Error: can't deduce the function type

Note that this is not the same question as this question. In that answer, the author writes a lambda expression without explicit template parameters.

std::cout << std::apply(
    [](auto first, auto second) { return add_generic(first, second); },
    std::make_tuple(2.0f,3.0f)) << '\n';

but as you know in c 20 you can have lambda expressions with explicit template parameter list. So I tried this feature to this case and surprisingly the compiler did not raise any errors.

std::apply([]<typename T>(T first,T second){
    return first second;
},std::make_pair(2.0,3.0));

Why will the compiler be able to deduce type in the last case? Is there any difference between the two?

CodePudding user response:

A function template is not a function, just like a cookie cutter is not a cookie. C templates create new functions for every set of template arguments. And this is precisely why add_generic is not a viable argument. It's not a function, it cannot be passed around as a value. To obtain the function, the compiler needs to deduce the template arguments. But it can only do that inside apply after the callable has been passed. Chicken and egg right there.

Why does a lambda work? Because it's not a plain function either. It produces a hidden object type

struct __lambda_at_line_N {
  template<typename T>
  auto operator()(T first, T second) { /* ... */ }
};

And passes that

std::apply(__lambda_at_line_N{},std::make_pair(2.0,3.0));

This is an actual object, a value. Its type does not depend on the template argument deduction that needs to happen inside std::apply to call operator(). That's why it works.

  • Related