Passing a lambda argument to a std::function parameter without intermediate variable - c++

This might look similar to "I cannot pass lambda as std::function", but I'm actually passing the std::function parameter by value, so that problem doesn't apply. I've defined the following function.
template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(T)> keyFunc, int n);
The second parameter is an std::function that maps T to int (passed by value).
When calling this, I wanted to use a lambda expression, as follows:
std::vector<int> v;
[...]
v = countSort(v, [](int x) { return x; }, 10);
But the template argument deduction fails, because "main()::<lambda(int)> is not derived from std::function<int(T)>". It does work if I specify the template argument, or if I introduce an intermediate variable of type std::function for the lambda expression:
std::function<int(int)> lambda = [](int x) { return x; };
v = countSort(v, lambda, 10);
Why can't I do the former? I'm giving the compiler the exact same information; if it is able to convert a value of type lambda<int> to std::function<int(int)> when assigning it to a variable, why can't it directly convert from lambda<int> to the parameter type, which is std::function<T(int)>—and taking into account that v is of type std::vector<int>, it should know that T is int? The whole reason I want to use a lambda expression is precisely that, it's an expression, so I should be able to write it inline in the function call argument list, without having to give it a name or assign it to a variable.

The problem is, template argument deduction doesn't consider implicit conversion (from lambda to std::function), which causes the deduction for T on the 2nd function parameter keyFunc to fail.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can use std::type_identity (since C++20) to exclude the 2nd function parameter from deduction. e.g.
template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(std::type_identity_t<T>)> keyFunc, int n);
BTW: If your compiler doesn't support std::type_identity, it's not hard to make one.
And about how std::type_identity works here, see non-deduced context:
(emphasis mine)
In the following cases, the types, templates, and non-type values that
are used to compose P do not participate in template argument
deduction, but instead use the template arguments that were either
deduced elsewhere or explicitly specified. If a template parameter is
used only in non-deduced contexts and is not explicitly specified,
template argument deduction fails.
The nested-name-specifier (everything to the left of the scope
resolution operator ::) of a type that was specified using a
qualified-id:

Related

Could not deduce template argument (vector, std::function)

I created a templated function of which I am trying to automatically deduce the template argument.
MCVE(compile it):
template<class Value, class Allocator>
void foo(const std::vector<Value, Allocator>& v, const std::function<void(const Value&)>& f)
{
}
int main()
{
vector<int> v;
foo<int>(v, [](const int&){}); //okay
//foo(v, [](const int&){}); //not okay
return 0;
}
I first thought the Allocator could not be deduced but that does not seem to solve it.
My next guess is it has something to do with the lambda to std::function but no idea on further steps there. Anybody got any clues about what I need to do to make this deducible?
Ps: I know "const int&" could become "int" but in the real code there is a non scalar data type there.
Template argument deduction happens before implicit conversion of Lambda to std::fucntion.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Meaning type deduction of the template parameter Value on the 2nd function argument f fails because the implicit conversion from lambda to std::function are not considered.
As you showed you can specify template arguments explicitly (to bypass the template argument deduction). Or you can exclude the 2nd argument from deduction by using std::type_identity to declare it as non deduced context.
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
e.g.
template<class Value, class Allocator>
void foo(const std::vector<Value, Allocator>& v, const std::function<void(const std::type_identity_t<Value>&)>& f)
{
}
LIVE
PS: std::type_identity is supported from C++20; if the compiler you're using doesn't support it it's not hard to make one.

Substitution failure with `std::function` and previously deduced template parameter - why?

Consider the following code:
template <typename>
struct S { };
void g(S<int> t);
template <typename T>
void f(T, std::function<void(S<T>)>);
When attempting to invoke
f(0, g);
I get the following error:
error: no matching function for call to 'f'
f(0, g);
^
note: candidate template ignored: could not match
'function<void (S<type-parameter-0-0>)>'
against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
^
live example on godbolt.org
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context
In this case T can first be deduced by the passed argument 0, and then substituted into std::function<void(S<T>)> to get std::function<void(S<int>)>.
I would expect that after deducing T=int, the compiler would substitute T everywhere in the signature and then attempt to construct the std::function parameter with the argument g.
Why is that not the case? I presume that the ordering in which substitution/deduction happens has something to do with this, but I'd like to see the relevant Standard wording.
Bonus question: is this something that could potentially be changed in a future Standard while preserving backwards compatibility, or is there a fundamental reason why this kind of substitution doesn't work?
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context.
It is not a non-deduced context. Quite the contrary. Because deduction for the parameter of std::function is attempted, but the argument is not a std::function, deduction fails. The deduction of template arguments from function arguments must agree for all function arguments. If it fails for one, it fails entirely.
[temp.deduct.type]
2 In some cases, the deduction is done using a single set of
types P and A, in other cases, there will be a set of corresponding
types P and A. Type deduction is done independently for each P/A pair,
and the deduced template argument values are then combined. If type
deduction cannot be done for any P/A pair, or if for any pair the
deduction leads to more than one possible set of deduced values, or if
different pairs yield different deduced values, or if any template
argument remains neither deduced nor explicitly specified, template
argument deduction fails.
Making the type of the second function parameter into a non-deduced context is actually how one can overcome the error.
#include <functional>
template<typename T>
struct type_identity {
using type = T;
};
template <typename>
struct S { };
void g(S<int> ) {}
template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}
int main() {
f(0, g);
}
T is deduced successfully from the first function argument, and there is nothing left to deduce. So the dedcution is deemed a success.
Live
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context, in this case T can first be deduced by the passed argument 0.
This is not true. T is deduceable in this context. If you change the code to
template <typename T>
void f(std::function<void(S<T>)>);
int main()
{
f(std::function<void(S<int>)>(g));
}
the code would compile and T is correctly deduced.
Your issue is that you are passing an object to the function that it can't extract T from. The compiler will not do any conversion of the function arguments when it tries to deduce T. That means you have a int and a function as the types passed to the function. It gets int from 0, then tries to get the type from the std::function you pass in the second parameter but since you didn't pass a std::function it can't extract T and because of that, you get an error.

Array value type deduction in a function template

I have following concern regarding following code snippet:
template<std::size_t Dim, std::size_t N,
typename RangeType1, typename RangeType2>
void multilinear_interp(
const RangeType1 (&coordsFrom)[Dim],
const std::array<RangeType2, N>& field)
{
// do something
}
int main()
{
std::vector<double> x{}, y{};
std::vector<float> f0{}, f1{};
multilinear_interp<2, 2>({x, y}, {f0, f1});
}
It looks like compiler can deduce RangeType1, but cannot deduce RangeType2 and compilation fails. Is there a possibility to use std::array without explicitly specifying all template parameters of multiliner_interp? If not then what is special about const T (&)[Dim] w.r.t std::array that it can be deduced in above context?
Thank you in advance.
Yeah, there's no deducing a container like std::array from {f0, f1} or even {{f0, f1}} (if one's initial thought is brace elision). The reason is that a brace enclosed initializer is not an expression. It doesn't have a type to deduce from. So it's classified in general as a non-deduced context (i.e. template arguments cannot be deduced from it).
As for why it works in the case of a regular C-style array reference, it's because an exception is made explicitly for them and std::initializer_list parameters. All of this is described in the same standard paragraph:
[temp.deduct.call]
1 Template argument deduction is done by comparing each function
template parameter type (call it P) that contains template-parameters
that participate in template argument deduction with the type of the
corresponding argument of the call (call it A) as described below. If
removing references and cv-qualifiers from P gives
std​::​initializer_­list<P'> or P'[N] for some P' and N and the
argument is a non-empty initializer list ([dcl.init.list]), then
deduction is performed instead for each element of the initializer
list, taking P' as a function template parameter type and the
initializer element as its argument, and in the P'[N] case, if N is a
non-type template parameter, N is deduced from the length of the
initializer list. Otherwise, an initializer list argument causes the
parameter to be considered a non-deduced context ([temp.deduct.type]).

Auto detection of template parameters without arguments

This is an offshoot of an answer to another SO post.
I have the following working code, with the expected output.
#include <iostream>
template <typename T>
T twice(T in)
{
return 2*in;
}
struct Foo
{
Foo operator+(int (*func)(int in)) const
{
Foo ret{data};
ret.data += func(ret.data);
return ret;
}
int data;
};
int main()
{
Foo f1{20};
Foo f2 = f1 + twice;
Foo f3 = f1 + twice<int>;
std::cout << f2.data << std::endl;
std::cout << f3.data << std::endl;
}
I did not know until yesterday that the compiler can deduce the type parameters of a function template even without an argument. In the code above, the expressions
f1 + twice
and
f1 + twice<int>
result in identical values.
My question is: Where in the C++03/C++11 standard can we find the necessary supporting documentation for the compiler's auto type detection logic?
C++11 14.8.2.2 Template arguments can be deduced from the type specified when taking the address of an overloaded function.
Here, the type specified by the parameter type of operator+ is int (*)(int), the overloaded function is twice, so int is deduced as the template argument to give a matching function type. See 14.8.2.5 if you need the gory details of that deduction.
This is closest to the actual c++11 standard but still openly available version of draft I've found: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf.
I believe the template parameter deduction is done when you write Foo f2 = f1 + twice; and pass twice as a function address. twice gets passed as an address of a function to the operator+. I believe the following mechanism kicks in:
14.8.2.2 Deducing template arguments taking the address of a function template
[temp.deduct.funcaddr]:
Template arguments can be deduced from the type specified when taking the address of an overloaded function (13.4). The function template’s function type and the specified type are used as the types of P and A, and the deduction is done as described in 14.8.2.5.
So the actual template deduction will be done basing on the type of the funct. I believe the relevant paragraphs from 14.8.2.5 are 1 and 2.
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]`:
1 Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
2 In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template
argument deduction fails.
Basically you pass twice as a pointer to the operator+, and then template arguments get deduced via type of the function as defined in operator+. So you have an actual type A that is int (*)(int in) and template types P of twice that gets matched against A and only int twice(int) fits.
I hope I got everything right.

Why can't my C++ compiler deduce template argument for boost function?

I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}