Passing a lambda function to a template method - c++

I have the following templated method:
auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>
template<class T>
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}
And I have a function that looks like
auto comp1 = [&](
const std::pair<std::vector<long>, math::Vector3f>& n1,
const std::pair<std::vector<long>, math::Vector3f>& n2
) -> int {
return 0;
};
math::eraserFunction(clusters, comp1);
However, I get a syntax error saying:
116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
| ^~~~~~~~~~~~~~
core.hpp:116:6: note: template argument deduction/substitution failed:
geom.cpp:593:23: note: 'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
593 | math::eraserFunction(clusters, comp1);

The function call tries to deduce T from both the first and second function parameter.
It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.
If deduction isn't possible from all parameters that are deduced context, deduction fails.
You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:
void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)
This requires C++20, but can be implemented easily in user code as well if you are limited to C++11:
template<typename T>
struct type_identity { using type = T; };
and then
void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)
std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.
If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:
template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)
This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C++20 a type constraint to enforce this already at overload resolution time.

Related

"template argument deduction/substitution failed" error with function object with parameter pack

I'm trying to make a function that takes a variable number of parameters of any type, but even the simple example I made is getting an error
#include <iostream>
#include <functional>
template<class... Ts>
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
{
function(parameters...);
}
void myFunc(const std::string& output)
{
std::cout << output << std::endl;
}
int main()
{
callFunction<const std::string&>(&myFunc, "Hello world");
return 0;
}
When I run the above code in Ideone, I get this error:
prog.cpp: In function ‘int main()’:
prog.cpp:17:57: error: no matching function for call to ‘callFunction(void (*)(const string&), const char [12])’
callFunction<const std::string&>(&myFunc, "Hello world");
^
prog.cpp:5:6: note: candidate: template<class ... Ts> void callFunction(const std::function<void(Ts ...)>&, Ts ...)
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
^~~~~~~~~~~~
prog.cpp:5:6: note: template argument deduction/substitution failed:
prog.cpp:17:57: note: mismatched types ‘const std::function<void(Ts ...)>’ and ‘void (*)(const string&) {aka void (*)(const std::__cxx11::basic_string<char>&)}’
callFunction<const std::string&>(&myFunc, "Hello world");
A simple suggestion: receive the callable as a deduced typename, not as a std::function
I mean (adding also perfect forwarding)
template <typename F, typename ... Ts>
void callFunction(F const & func, Ts && ... pars)
{ func(std::forward<Ts>(pars)...); }
and, obviously, call it without explicating nothing
callFunction(&myFunc, "Hello world");
This as the additional vantage that avoid the conversion of the callable to a std::function.
Anyway, I see two problems in your code:
1) if you receive the functional as a std::function receiving a list ot arguments types (a variadic list in this case, but isn't important for this problem) as a list of argument of the same types, you have to be sure that the types in the two list match exactly.
This isn't your case because the function receive a std::string const & and you pass as argument a the string literal "Hello world" that is a char const [12] that is a different type.
When the types are to be deduced, this cause a compilation error because the compiler can't choose between the two types.
You could solve receiving two list of types
template <typename ... Ts1, typename Ts2>
void callFunction (std::function<void(Ts1...)> const & function,
Ts2 && ... parameters)
{ function(std::forward<Ts2>(parameters)...); }
but now we have the second problem
2) You pass a pointer function (&myFunc) where callFunction() wait for a std::function.
We have a chicken-egg problem because &myFunc can be converted to a std::function but isn't a std::function.
So the compiler can't deduce the Ts... list of types from &myFunc because isn't a std::function and can't convert &myFunc to a std::function because doesn't know the Ts... type list.
I see that you have explicated the first type in the Ts... list, but isn't enough because the Ts... list is a variadic one so the compiler doesn't know that there is only a type in the Ts... list.
A simple solution to this problem is pass the function as a simple deduced F type.
Otherwise, if you have written callFunction() with two templates types lists, you can pass a std::function to the function
std::function<void(std::string const &)> f{&myFunc};
callFunction(f, "Hello world");
but I don't think is a satisfactory solution.

std::bind on a generic lambda - auto type deduction

Consider the following code:
#include <iostream>
#include <functional>
int main() {
auto run = [](auto&& f, auto&& arg) {
f(std::forward<decltype(arg)>(arg));
};
auto foo = [](int &x) {};
int var;
auto run_foo = std::bind(run, foo, var);
run_foo();
return 0;
}
Which gives the following compilation error when compiled with clang:
$ clang++ -std=c++14 my_test.cpp
my_test.cpp:6:9: error: no matching function for call to object of type 'const (lambda at my_test.cpp:8:16)'
f(std::forward<decltype(arg)>(arg));
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:998:14: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const (lambda at my_test.cpp:8:16) &, const int &>' requested here
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:1003:2: note: in instantiation of default argument for 'operator()<>' required here
operator()(_Args&&... __args) const
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_test.cpp:11:12: note: while substituting deduced template arguments into function template 'operator()' [with _Args = <>, _Result = (no value)]
run_foo();
^
my_test.cpp:8:16: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
auto foo = [](int &x) {};
^
my_test.cpp:8:16: note: conversion candidate of type 'void (*)(int &)'
1 error generated.
Why is arg deduced to be const int& instead of just int&?
std::bind documentation says:
Given an object g obtained from an earlier call to bind, when it is
invoked in a function call expression g(u1, u2, ... uM), an invocation
of the stored object takes place, as if by std::invoke(fd,
std::forward(v1), std::forward(v2), ...,
std::forward(vN)), where fd is a value of type std::decay_t the
values and types of the bound arguments v1, v2, ..., vN are determined
as specified below.
...
Otherwise, the
ordinary stored argument arg is passed to the invokable object as
lvalue argument: the argument vn in the std::invoke call above is
simply arg and the corresponding type Vn is T cv &, where cv is the
same cv-qualification as that of g.
But in this case, run_foo is cv-unqualified. What am I missing?
MWE:
#include <functional>
int main() {
int i;
std::bind([] (auto& x) {x = 1;}, i)();
}
[func.bind]/(10.4) states that the cv-qualifiers of the argument passed to the lambda are those of the argument to bind, augmented by the cv-qualifiers of the call wrapper; but there are none, and thus a non-const int should be passed in.
Both libc++ and libstdc++ fail to resolve the call. For libc++, reported as #32856, libstdc++ as #80564. The main problem is that both libraries infer the return type in the signature somehow, looking like this for libstdc++:
// Call as const
template<typename... _Args, typename _Result
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
typename add_const<_Functor>::type&>::type>()(
_Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
std::declval<tuple<_Args...>&>() )... ) )>
_Result operator()(_Args&&... __args) const
During template argument deduction as necessitated by overload resolution, the default template argument will be instantiated, which causes a hard error due to our ill-formed assignment inside the closure.
This can be fixed by perhaps a deduced placeholder: remove _Result and its default argument entirely, and declare the return type as decltype(auto). This way, we also get rid of SFINAE which influences overload resolution and thereby induces incorrect behaviour:
#include <functional>
#include <type_traits>
struct A {
template <typename T>
std::enable_if_t<std::is_const<T>{}> operator()(T&) const;
};
int main() {
int i;
std::bind(A{}, i)();
}
This should not compile—as explained above, the argument passed to A::operator() should be non-const because i and the forwarding call wrapper are. However, again, this compiles under libc++ and libstdc++, because their operator()s fall back on const versions after the non-const ones fail under SFINAE.

Pass lambda as template function parameter

Why doesn't the following code compile (in C++11 mode)?
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, [](const T&) { return 42; });
}
The error message is:
prog.cc:9:5: error: no matching function for call to 'qux'
qux(ts, [](const T&) { return 42; });
^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
^
But it doesn't explain why it couldn't match the parameter.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:
qux(ts, +[](const T&) { return 42; });
As noted in the comments, the best you can do to get it out from a lambda is this:
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, *+[](const T&) { return 42; });
}
int main() {}
Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.
If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:
template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }
Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.
As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.
You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.
Also, lambda here needs to be passed by pointer.
Finally, this version compiles ok:
template<typename From, typename To>
void qux(const std::vector<From>&, To (*)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux<T,int>(ts,[](const T&) { return 42; });
}
You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.
But it doesn't explain why it couldn't match the parameter.
Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.
In this expression:
qux(ts, [](const T&) { return 42; });
The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.
template <class From, class To>
void func_tmpl(From(*)(To) ) { }
void func_normal(int(*)(int ) ) { }
func_tmpl([](int i){return i; }); // error
func_tmpl(+[](int i){return i; }); // ok, we force the conversion ourselves,
// the type of this expression can be deduced
func_normal([](int i){return i; }); // ok, implicit conversion
This is the same reason why this fails:
template <class T> void foo(std::function<T()> );
foo([]{ return 42; }); // error, this lambda is NOT a function<T()>
But this succeeds:
void bar(std::function<int()> );
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()>
The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:
template <class From,
class F&&,
class To = std::result_of_t<F&&(From const&)>>
void qux(std::vector<From> const&, F&& );
Now you can pass your lambda, or function, or function object just fine.

Passing Member Function pointer using variadic template arguments is ambiguous

Why is "TArgs" ambiguous in my Example?
The compiler should know the only existing function signature
virtual CGame::NetCreatePlayer(CNetPeer* _pPeer, int _ObjectID, const CNetMessage& _ObjectData, bool _bAuthority);
and deduce correct TArgs in this function:
template<typename... TArgs >
void INetInterface<TSubClass>::NetCall(void(TSubClass::*_pFunc)(CNetPeer*, TArgs...), CNetPeer* _pPeer, TArgs... _Params);
I want to call it like this:
CNetMessage obj;
//...
NetCall(&CGame_Server::NetCreatePlayer, _pClient, 0, obj, true);
CGame_Server inherits CGame.
Compiler output:
4> error C2782: 'void INetInterface<CGame>::NetCall(void (__thiscall CGame::* )(CNetPeer *,TArgs...),CNetPeer *,TArgs...)'
: template parameter 'TArgs' is ambiguous
4> NetInterface.h(82) : see declaration of INetInterface<CGame>::NetCall'
4> could be 'int, const CNetMessage&, bool'
4> or 'int, CNetMessage, bool'
It can't be 'int, CNetMessage, bool', right?
Is there a way to get around this problem?
I tried casting to const CNetMessage& but strangely enough that does not help.
And no there are no other member functions with that same name.
NetCall(&CGame_Server::NetCreatePlayer, _pClient, 0, obj, true);
There are two places in this call from which TArgs can be deduced:
From the member function pointer's type, the compiler deduces TArgs == int, const CNetMessage&, bool
From the parameters 0, obj, true, the compiler deduces TArgs == int, CNetMessage, bool
The results conflict. To fix this, use the identity trick to put the second TArgs into a non-deduced context:
template<class T> struct identity { using type = T; };
template<class T> using identity_t = typename identity<T>::type;
template<typename... TArgs >
void INetInterface<TSubClass>::NetCall(void(TSubClass::*_pFunc)(CNetPeer*, TArgs...),
CNetPeer* _pPeer, identity_t<TArgs>... params);
As a side note, _Params is a reserved identifier, along with _ObjectData and _ObjectID; you should rename them.

Odd behavior when recursively building a return type for variadic functions

This is probably going to be a really simple explanation, but I'm going to give as much backstory as possible in case I'm wrong. Advanced apologies for being so verbose. I'm using gcc4.5, and I realize the c++0x support is still somewhat experimental, but I'm going to act on the assumption that there's a non-bug related reason for the behavior I'm seeing.
I'm experimenting with variadic function templates. The end goal was to build a cons-list out of std::pair. It wasn't meant to be a custom type, just a string of pair objects. The function that constructs the list would have to be in some way recursive, with the ultimate return value being dependent on the result of the recursive calls. As an added twist, successive parameters are added together before being inserted into the list. So if I pass [1, 2, 3, 4, 5, 6] the end result should be {1+2, {3+4, 5+6}}.
My initial attempt was fairly naive. A function, Build, with two overloads. One took two identical parameters and simply returned their sum. The other took two parameters and a parameter pack. The return value was a pair consisting of the sum of the two set parameters, and the recursive call. In retrospect, this was obviously a flawed strategy, because the function isn't declared when I try to figure out its return type, so it has no choice but to resolve to the non-recursive version.
That I understand. Where I got confused was the second iteration. I decided to make those functions static members of a template class. The function calls themselves are not parameterized, but instead the entire class is. My assumption was that when the recursive function attempts to generate its return type, it would instantiate a whole new version of the structure with its own static function, and everything would work itself out.
The result was: "error: no matching function for call to BuildStruct<double, double, char, char>::Go(const char&, const char&)"
The offending code:
static auto Go(const Type& t0, const Type& t1, const Types&... rest)
-> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))>
My confusion comes from the fact that the parameters to BuildStruct should always be the same types as the arguments sent to BuildStruct::Go, but in the error code Go is missing the initial two double parameters. What am I missing here? If my initial assumption about how the static functions would be chosen was incorrect, why is it trying to call the wrong function rather than just not finding a function at all? It seems to just be mixing types willy-nilly, and I just can't come up with an explanation as to why. If I add additional parameters to the initial call, it always burrows down to that last step before failing, so presumably the recursion itself is at least partially working. This is in direct contrast to the initial attempt, which always failed to find a function call right away.
Ultimately, I've gotten past the problem, with a fairly elegant solution that hardly resembles either of the first two attempts. So I know how to do what I want to do. I'm looking for an explanation for the failure I saw.
Full code to follow since I'm sure my verbal description was insufficient. First some boilerplate, if you feel compelled to execute the code and see it for yourself. Then the initial attempt, which failed reasonably, then the second attempt, which did not.
#include <iostream>
using std::cout;
using std::endl;
#include <utility>
template<typename T1, typename T2>
std::ostream& operator <<(std::ostream& str, const std::pair<T1, T2>& p) {
return str << "[" << p.first << ", " << p.second << "]";
}
//Insert code here
int main() {
Execute(5, 6, 4.3, 2.2, 'c', 'd');
Execute(5, 6, 4.3, 2.2);
Execute(5, 6);
return 0;
}
Non-struct solution:
template<typename Type>
Type BuildFunction(const Type& t0, const Type& t1) {
return t0 + t1;
}
template<typename Type, typename... Rest>
auto BuildFunction(const Type& t0, const Type& t1, const Rest&... rest)
-> std::pair<Type, decltype(BuildFunction(rest...))> {
return std::pair<Type, decltype(BuildFunction(rest...))>
(t0 + t1, BuildFunction(rest...));
}
template<typename... Types>
void Execute(const Types&... t) {
cout << BuildFunction(t...) << endl;
}
Resulting errors:
test.cpp: In function 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]':
test.cpp:33:35: instantiated from here
test.cpp:28:3: error: no matching function for call to 'BuildFunction(const int&, const int&, const double&, const double&, const char&, const char&)'
Struct solution:
template<typename... Types>
struct BuildStruct;
template<typename Type>
struct BuildStruct<Type, Type> {
static Type Go(const Type& t0, const Type& t1) { return t0 + t1; }
};
template<typename Type, typename... Types>
struct BuildStruct<Type, Type, Types...> {
static auto Go(const Type& t0, const Type& t1, const Types&... rest)
-> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))> {
return std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))>
(t0 + t1, BuildStruct<Types...>::Go(rest...));
}
};
template<typename... Types>
void Execute(const Types&... t) {
cout << BuildStruct<Types...>::Go(t...) << endl;
}
Resulting errors:
test.cpp: In instantiation of 'BuildStruct<int, int, double, double, char, char>':
test.cpp:33:3: instantiated from 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]'
test.cpp:38:41: instantiated from here
test.cpp:24:15: error: no matching function for call to 'BuildStruct<double, double, char, char>::Go(const char&, const char&)'
test.cpp:24:15: note: candidate is: static std::pair<Type, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...))> BuildStruct<Type, Type, Types ...>::Go(const Type&, const Type&, const Types& ...) [with Type = double, Types = {char, char}, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...)) = char]
test.cpp: In function 'void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]':
test.cpp:38:41: instantiated from here
test.cpp:33:3: error: 'Go' is not a member of 'BuildStruct<int, int, double, double, char, char>'
Reading the comments, it seems clear enough that this is a very localized bug in a particular version of G++, and that's all the answer there will ever be.