std::function in template function - c++

I was trying to write a template function which can accept functor as parameter and call it afterwards. The program is as follows:
#include <iostream>
#include <functional>
using namespace std;
template<typename R, typename... Args>
R call(function<R(Args...)> fun, Args... args)
{
cout << "call# " << __LINE__ <<endl;
return fun(args...);
}
int main()
{
cout << call(std::plus<int>(),1,2) <<endl;
return 0;
}
The G++ compplains:
g++ -c -Wall -std=c++0x -I../include a.cpp -o a.o
a.cpp: In function ‘int main()’:
a.cpp:16:38: error: no matching function for call to ‘call(std::plus<int>, int, int)’
a.cpp:16:38: note: candidate is:
a.cpp:7:3: note: template<class R, class ... Args> R call(std::function<_Res(_ArgTypes ...)>, Args ...)
a.cpp:7:3: note: template argument deduction/substitution failed:
a.cpp:16:38: note: ‘std::plus<int>’ is not derived from ‘std::function<_Res(_ArgTypes ...)>’
make: *** [a.o] Error 1
I suppose std::plus<int>() could be deduced to std::function<int(int,int)>, but it didn't. Why was that? GCC is gcc version 4.7.2 20120921 (Red Hat 4.7.2-2) (GCC)

I suppose std::plus() could be deduced to std::function
No. It could not be deduced given that you have passed an object of type std::plus<int>.
In your case, you do not need to use std::function, as generally you would mostly use it when storing different functions/function objects that can be called with a specific signature.
With that, you can just have your call function accept the function/function object directly, with its original type deduced, without using std::function. Also, you might also want to use perfect forwarding when accepting the parameters and use std::forward when passing them as arguments to the function/function object. You should also use the return type of your function as the return type of call. Use C++11's trailing return type with decltype for that.
#include <iostream>
#include <functional>
using namespace std;
template<typename R, typename... Args>
auto call(R fun, Args&&... args) -> decltype(fun(std::forward<Args>(args)...))
{
cout << "call# " << __LINE__ <<endl;
return fun(std::forward<Args>(args)...);
}
int main()
{
cout << call(std::plus<int>(),1,2) <<endl;
return 0;
}
LIVE CODE
As what #Jan Hudec has commented, __LINE__ in there will always result the same in all calls to call, whatever function is passed.

It can't deduce the template arguments.
I would recommend changing the function signature like so:
template<typename F, typename... Args>
auto call(F fun, Args... args )
-> decltype( fun(args...) )

Most implicit conversions are not considered when deducing template arguments. Certainly not user-defined ones. So even if plus is convertible to function, it doesn't make a difference.

Related

Why compile give note in std::initializer_list regarding function template specialization?

I wrote a printf function using std::initializer_list:
template<typename T, typename... Ts>
auto printf(T t, Ts... args) {
std::cout << t << std::endl;
return std::initializer_list<T>{([&] {
std::cout << args << std::endl;
}(), t)...};
}
int main() {
printf(111, 123, "alpha", 1.2);
return 0;
}
The compiler gives a note on instantiation of function template specialization:
warning: returning address of local temporary object [-Wreturn-stack-address]
return std::initializer_list<T>{([&] {
^~~~~~~
note: in instantiation of function template specialization 'printf<int, int, const char *, double>' requested here
printf(111, 123, "alpha", 1.2);
I aware of returning stack address is a bad practice, however, if I don't do return then I will receive:
warning: expression result unused [-Wunused-value]
How could I change my code to avoid these three type of compiler warnings?
Cast initializer_list to void to show the compiler you do not use it intentionally
How could I change my code to avoid these three type of compiler warnings?
Don't return the object and use the good old (void)-trick (see e.g. this thread for more info on that):
(void) std::initializer_list<T>{ /* ... */ };
By the way, pretty awesome printf implementation :)

"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.

Failure to deduce template argument std::function from lambda function

While exploring templates in C++, I stumbled upon the example in the following code:
#include <iostream>
#include <functional>
template <typename T>
void call(std::function<void(T)> f, T v)
{
f(v);
}
int main(int argc, char const *argv[])
{
auto foo = [](int i) {
std::cout << i << std::endl;
};
call(foo, 1);
return 0;
}
To compile this program, I am using the GNU C++ Compiler g++:
$ g++ --version // g++ (Ubuntu 6.5.0-1ubuntu1~16.04) 6.5.0 20181026
After compiling for C++11, I get the following error:
$ g++ -std=c++11 template_example_1.cpp -Wall
template_example_1.cpp: In function ‘int main(int, const char**)’:
template_example_1.cpp:15:16: error: no matching function for call to ‘call(main(int, const char**)::<lambda(int)>&, int)’
call(foo, 1);
^
template_example_1.cpp:5:6: note: candidate: template<class T> void call(std::function<void(T)>, T)
void call(std::function<void(T)> f, T v)
^~~~
template_example_1.cpp:5:6: note: template argument deduction/substitution failed:
template_example_1.cpp:15:16: note: ‘main(int, const char**)::<lambda(int)>’ is not derived from ‘std::function<void(T)>’
call(foo, 1);
^
(same for C++14 and C++17)
From the compiler error and notes I understand that the compiler failed to deduce the type of the lambda, since it cannot be matched against std::function.
Looking at previous questions (1, 2, 3, and 4) regarding this error, I am still confused about it.
As pointed out in answers from questions 3 and 4, this error can be fixed by explicitly specifying the template argument, like so:
int main(int argc, char const *argv[])
{
...
call<int>(foo, 1); // <-- specify template argument type
// call<double>(foo, 1) // <-- works! Why?
return 0;
}
However, when I use other types instead of int, like double, float, char, or bool, it works as well, which got me more confused.
So, my questions are as follow:
Why does it work when I explicitly specify int (and others) as the template argument?
Is there a more general way to solve this?
A std::function is not a lambda, and a lambda is not a std::function.
A lambda is an anonymous type with an operator() and some other minor utility. Your:
auto foo = [](int i) {
std::cout << i << std::endl;
};
is shorthand for
struct __anonymous__type__you__cannot__name__ {
void operator()(int i) {
std::cout << i << std::endl;
}
};
__anonymous__type__you__cannot__name__ foo;
very roughly (there are actual convert-to-function pointer and some other noise I won't cover).
But, note that it does not inherit from std::function<void(int)>.
A lambda won't deduce the template parameters of a std::function because they are unrelated types. Template type deduction is exact pattern matching against types of arguments passed and their base classes. It does not attempt to use conversion of any kind.
A std::function<R(Args...)> is a type that can store anything copyable that can be invoked with values compatible with Args... and returns something compatible with R.
So std::function<void(char)> can store anything that can be invoked with a char. As int functions can be invoked with a char, that works.
Try it:
void some_func( int x ) {
std::cout << x << "\n";
}
int main() {
some_func('a');
some_func(3.14);
}
std::function does that some conversion from its signature to the callable stored within it.
The simplest solution is:
template <class F, class T>
void call(F f, T v) {
f(v);
}
now, in extremely rare cases, you actually need the signature. You can do this in c++17:
template<class T>
void call(std::function<void(T)> f, T v) {
f(v);
}
template<class F, class T>
void call(F f_in, T v) {
std::function f = std::forward<F>(f_in);
call(std::move(f), std::forward<T>(v));
}
Finally, your call is a crippled version of std::invoke from c++17. Consider using it; if not, use backported versions.

Detecting parameter types from generic lambda - compile error with GCC

I wrote some code that retrieves the types of the non-auto parameters when given a generic lambda function. As you can see in the code below, the idea is to call the connect function with a generic lambda and provide arguments for the auto parameters (which will always be at the front in my use case). So in the code below my goal was to detect that the second parameter is of type float.
The code works fine with clang 3.8 but it doesn't compile with gcc 6.1.1, so I was wondering whether this was a bug in gcc or if this is just not valid c++ code? Can I assume that a generic lambda is implemented with a templated operator() function or is this compiler-specific?
template <typename Functor, typename... AllArgs, typename... ProvidedArgs>
void findArgTypes(void(Functor::*)(AllArgs...) const, Functor, ProvidedArgs...)
{
// AllArgs == int, float
// ProvidedArgs == int
}
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
}
int main()
{
int tmp = 0;
connect([&](auto, float){ ++tmp; }, 0);
}
The error that gcc gives is this:
main.cpp: In instantiation of ‘void connect(Func, ProvidedArgs ...) [with Func = main()::<lambda(auto:1, float)>; ProvidedArgs = {int}]’:
main.cpp:16:33: required from here
main.cpp:11:17: error: no matches converting function ‘operator()’ to type ‘void (struct main()::<lambda(auto:1, float)>::*)() const’
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:27: note: candidate is: template<class auto:1> main()::<lambda(auto:1, float)>
connect([](auto, float){}, 0);
^
Removing the const in findArgTypes gives the same result.
Using the following code works with both compilers:
struct Foo
{
template <typename T>
void operator()(T, float) const {}
};
int main()
{
Foo f;
connect(f, 0);
}
You have error because you are expecting functor (object) but lambda with empty capture is convertible to free function:
int main() {
using function = void (*)(int, float);
function a = [](auto, float){};
}
See lambda from cppreference:
For the newest version of your question that implementation satisfies both compilers:
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
auto mf = &Func::template operator()<ProvidedArgs...>;
findArgTypes(mf, func, providedArgs...);
}
I think this is gcc compiler bug that gcc needs this auto local variable to work correctly...
BTW, one question - one bug in clang, one in gcc - I really advice you to find simpler way to achieve your goals - maybe consider to just use std::function instead of quite fresh generic-lambda?

Constructing class with member tuple containing rvalue references

When trying to construct a class which is supposed to hold a tuple created by calling std::forward_as_tuple I ran into the following error when compiling with clang(187537) and libc++:
/usr/include/c++/v1/tuple:329:11: error: rvalue reference to type 'int' cannot
bind to lvalue of type 'int'
: value(__t.get())
^ ~~~~~~~~~
/usr/include/c++/v1/tuple:447:8: note: in instantiation of member function
'std::__1::__tuple_leaf<0, int &&, false>::__tuple_leaf' requested here
struct __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
^
tuple.cpp:31:5: note: in instantiation of function template specialization
'make_foo2<int>' requested here
make_foo2(1 + 1);
^
In file included from tuple.cpp:2:
/usr/include/c++/v1/tuple:330:10: error: static_assert failed "Can not copy a
tuple with rvalue reference member"
{static_assert(!is_rvalue_reference<_Hp>::value, "Can not copy ...
I was able to work around the above error by declaring the return type differently, but, from my understanding, it should have the same semantics so I would not expect it to stop the error. In the below code make_foo is the workaround which does not error out and make_foo2 causes the above error. I am able to successfully compile both versions using gcc 4.8.1 and the version of clang at coliru.
#include <utility>
#include <tuple>
template<class Tuple>
struct foo
{
Tuple t;
foo(Tuple &&t) : t(std::move(t)) { }
};
template<class... Args>
using ForwardedTuple = decltype(std::forward_as_tuple(std::forward<Args>(std::declval<Args>())...));
template<class... Args>
foo<ForwardedTuple<Args...>> make_foo(Args&&... args)
{
return {std::forward_as_tuple(std::forward<Args>(args)...)};
}
template<class... Args>
auto make_foo2(Args&& ...args) ->
decltype(foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...)))
{
return foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...));
}
int main()
{
make_foo(1 + 1);
make_foo2(1 + 1);
}
What is the difference between the above make_foo functions and is make_foo2 incorrect?
Thanks.
Looks like you return foo<> from make_foo2. But foo doesn't have move constructor generated (Compiler won't generate it). Therefore copy constructor is called and compilation fails because of that.