Consider this simple (bad) function template for which lots of variations exist on this site:
template <typename R, typename... Args>
R call_with(std::function<R(Args...)> f,
Args... args)
{
return f(args...);
}
And two attempts at calling it:
call_with([]{}); // (a)
call_with<void>([]{}); // (b)
I cannot call (a) because a lambda is not a std::function<R(Args...)> so template deduction fails. Straightforward.
However, (b) also fails. I suspect this is because the compiler cannot determine that I mean to provide all the type arguments and reasons that I am simply providing R - so it is trying (and failing) to deduce Args... for the same reason that the initial call failed.
Is there a way to explicitly specify that I am providing all the template arguments? To clarify, I am interested only in how to explicitly provide the template arguments so that there is no template deduction - I am not looking for the correct way to write call_with or for a way to make template deduction succeed when called with a lambda.
The short answer to your--edited--question is: if you cannot change the declaration of call_with(), then either use the type casts demonstrated by #CoffeeandCode, or use the technique described below to create a wrapper for call_with().
The problem is in the fact that the compiler is trying to deduce the template arguments from the first function argument. You can prevent this if you write your code like this:
#include <functional>
#include <iostream>
// identity is a useful meta-function to have around.
// There is no std::identity, alas.
template< typename T>
struct identity
{
using type = T;
};
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
Args... args)
{
return f(args...);
}
int main()
{
call_with<void>([](int){ std::cout << "called" << std::endl; }, 2);
}
Using a template meta-function to "generate" the std::function type means that the compiler cannot even try to deduce the function type from the first argument and it will only use the other arguments.
You would still need to explicitly provide the return type of course, but for the other arguments you can now choose whether to explicitly specify them or leave it up to the compiler to deduce them from the arguments given.
If you really want to enforce that all template arguments are provided and not deduced, you can also wrap the argument pack in an invocation of identity this way:
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
typename identity<Args>::type... args)
In summary, if you want to prevent the compiler from deducing function template argument types that also appear as function parameters, wrap them in a meta function such as identity.
You could specify the function type beforehand, like this:
int main(){
std::function<void()> f = []{};
call_with(f);
}
or, in a little more messy but more compact way:
int main(){
call_with(static_cast<std::function<void()>>([]{}));
}
This is because the compiler doesn't know what return type and arguments to deduce for your template parameters before you ask it to implicitly convert a lambda, which is an unspecified function object defined by the compiler, to a std::function with those template parameters.
Really, you should just change your function warpper to be more generic:
template<typename Functor, typename ... Args>
auto wrapper(Functor &&f, Args &&... args) -> decltype(f(std::forward<Args>(args)...)){
return f(std::forward<Args>(args)...);
}
This should work for any function or functor type. It's also a really good example of the use of trailing return types.
Here's a live example
Related
Currently using g++11.3.0, C++20.
I'm trying to compile this bit of code below, where the function foo() is supposed to be able to take an arbitrary number of template arguments, and use them to provide arguments when calling the passed lambda func. I wanted to create a Callable concept that restricts func to only invocables. The code compiles fine as it is. But when I change the line template <typename ...T, typename Func> to template <typename ...T, Callable Func> to use the Callable concept, I get the following error:
error: no matching function for call to ‘foo<int, float, double>(main()::<lambda(int&&, float&&, double&&)>)’
Code:
#include <functional>
template <typename Func, typename ...Args>
concept Callable = requires (Func&& func, Args&&... args)
{
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
};
template <typename ...T, typename Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}
int main()
{
foo<int, float, double>([] (int&&, float&&, double&&) {});
return 0;
}
Declaring Callable Func will cause the compiler will only check whether Callable<F> is true, that is, whether F can be invoked without arguments.
You need to pass the argument types to Callable to constrain the single Func template parameter, for example
template <typename ...T, Callable<T...> Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}
I am trying to match callable objects in C++ templates and provide different implementations based on the type of callable object that is receives as an argument.
I have the following template functions:
template<class Ret, class... Args>
void fun(std::function<Ret(Args...)> f) {}
template<class Ret, class... Args>
void fun(Ret(*f)(Args...)) {}
They accept std::function objects and ordinary function pointers, and correctly deduce their return Ret and argument Args types. However, I am having difficulty passing anonymous lambda expressions as they do not match any of the templates above.
Calling something like
fun([](int x, int y){return x + y;})
results in a compilation error. What is the correct way to implement a template expression that accepts anonymous lambda functions? Maybe one way is to check if the class that is being passed has an operator() method?
Conversions happen after template argument substitution. The type of a lambda is not std::function, but an anonymous type. Indeed, you'll have the receive an anonymous lambda in order to receive any callable object.
It seems your concern is about restricting the object received to have the operator() defined. This problem is easily solvable with expression sfinae. However, since you're not receiving parameters, it will be harder to check if the expression is valid.
This is not a general example, but it will work for any lambda that does not have auto as one of it's parameter, or any object that has operator() not overloaded:
template<typename F>
auto fun(F f) -> void_t<decltype(&T::operator())> {
// ...
}
With void_t implemented as follow:
template<typename...>
using void_t = void;
The expression inside the decltype must be valid in order for the function to exist.
If you want your code to work with more types, you'll have to offer an overload that you can specify the parameters. The check is now trivial to implement:
// Another overload of your function
template<typename F, typename... Args>
auto fun(F f) -> void_t<decltype(f(std::declval<Args>()...)> {
// ...
}
That way, it will works for generic lambda and overloaded operators too.
This is valid C++ code, and gcc 6.1.1 at C++14 level will back me up on this:
template<typename F>
void foo(F &&f)
{
}
void bar()
{
auto lambda=[](auto a, auto b) {};
foo(lambda);
}
As you can see, a template can't possibly deduce the arguments of a lambda. The argument types to a lambda are not known until they are used.
You can deduce that something has an operator(), as the other answer shows, but that's pretty much as far as it goes.
I opened this post about forwarding reference, this is a (hopefully) MCVE code:
#include <functional>
#include <vector>
using namespace std;
struct MultiMemoizator {
template <typename ReturnType, typename... Args>
ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {
}
};
typedef vector<double> vecD;
vecD sort_vec (const vecD& vec) {
return vec;
}
int main()
{
vecD vec;
std::function<vecD(const vecD&)> sortFunc(sort_vec);
MultiMemoizator mem;
mem.callFunction<vecD, vecD>(sortFunc, vec);
}
Since this is not the whole code, maybe I'll have to add extra code based on the answers.
Anyway, as was suggested in this answer, forwarding reference is not possible with this version, since Args is not deduced.
So my question is: is it possible to make this code "forwarding referencable"?
In order to perfect-forward your arguments, you need to have the types deduced. You can do this by deducing the arguments to the function and the parameters to the functor separately:
template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
Args&&... args)
{
//...
}
Then you can call callFunction without template parameters and have everything deduced:
mem.callFunction(sortFunc, vec);
I will add a bit of details regarding #TartanLlama answer on why your code fails to compile (even without the explicit template parameters) but also why (in my own opinion) your code is dangerous.
In the following, I will use only a simple type T instead of your parameter pack Args... because it is simpler to explain and does not change the meaning.
A bit of reminder on forwarding references...
First, let's take a simpler example than yours with simply the following:
template <typename T>
void f (T&&);
Now, let's instanciate f from various sources, let's assume with have the following variables:
std::string s;
const std::string cs;
...then:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
You should be wondering: Why is the first instanciation f<std::string&> instead of f<std::string>?, but the standard tells you (§14.8.2.1#3 [temp.deduct.call]):
If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Back to our initial snippet!
Now, let's complicate a bit our example:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
And one instantiation:
std::string s;
A<std::string> as;
f(as, s);
The above is equivalent to your example, and will fails to compile, but why... ? Well, as explained above, when you have an lvalue, the deduced type for T&& is T&, not T, and thus the type deduction fails for A<T> because the compiler is expecting A<std::string&> and you are giving a A<std::string>.
So now we know that we have to do the following:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
Why is it dangerous?
Ok so now, this should be good:
A<std::string&&> arrs;
f(arrs, std::string());
But it is not... Because when T is deduced as a rvalue reference, T is simply T, so the compiler is expecting A<std::string>.
So here is the problem: You are going to give a rvalue to a method that is going to forward it to a function expecting an lvalue. That's not wrong, but it is probably not what you'd have expected.
How to deal with it?
The first possibility is to force the type of the first parameter regardless of the deduced type for T, e.g.:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
But note:
You would have to add more stuff to deal with const.
One may wonder the usefulness of T&& when the type of the first argument is fixed (in your case, at least).
The second possibility (warning: I don't know if this is standard!) is to move the first parameter at the end and then deduce the type from t:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
Now you have an exact match between the deduced type for T and the expected type for A.
Unfortunately I don't know how to make the above work with variadic templates...
I was trying to use some variadic template parameters but I was quickly stopped by an error I didn't managed to understand.
#include <tuple>
template <typename T>
struct Foo
{
typedef T type;
};
// return a tuple of pair of args and Foo templated on Types
template <typename Head, typename ...Args, typename Type, typename ...Types>
auto func(Head arg, Args... args)
{
return std::tuple_cat(std::make_tuple(std::make_pair(arg, Foo<Type>())),
func<Args..., Types...>(args...));
}
template <typename Head, typename Type>
auto func(Head arg)
{
return std::make_tuple(std::make_pair(arg, Foo<Type>()));
}
int main()
{
func<int, bool, char>(1, 2, 3);
}
Here func try to unpack template parameters and make a tuple of pair of a func argument and a Foo struct templated on the second variadic template but I am having:
test.cc:25:3: error: no matching function for call to 'func'
func<int, bool, char>(1, 2, 3);
^~~~~~~~~~~~~~~~~~~~~
test.cc:11:6: note: candidate template ignored: couldn't infer template argument 'Type'
auto func(Head arg, Args... args)
^
test.cc:18:6: note: candidate function template not viable: requires single argument 'arg', but 3
arguments were provided
auto func(Head arg)
^
1 error generated.
Why Type cannot be inferred ? (gcc told me the same)
I am sure that multiple variadic template is possible after seing std::tuple_cat implementation (https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-api-4.5/a01066_source.html) and I think this is a simple example and someone should knows better that me if there is a solution or if the standard doesn't accept this.
Thanks for your help,
Put deduced arguments last.
... template arguments are greedy -- they will consume passed in arguments and wont "save" any for later template arguments.
Once that is done, deduction occurs from non-template function arguments.
Swap like this:
template <typename Type, typename ...Types, typename Head, typename ...Args>
auto func(Head arg, Args... args)
Also, get rid of the other overload, so func<int,int>(3) is not ambiguous.
This makes the recursion break, but that is easy to fix:
template <class... Types, class... Args>
auto func(Args... args)
{
return std::make_tuple(std::make_pair(args, Foo<Types>())...);
}
Which has the bonus of being nice and short.
A key data point here is that a variadic parameter will be matched by an empty list. This has several consequences.
The first consequence is that given the following template declaration:
template <typename Head, typename ...Args, typename Type, typename ...Types>
and the following instantiation:
<int, bool, char>
There are several ways the instantiation can match the template:
1.
Head int
...Args bool
Type char
... Types (empty list)
2.
Head int
...Args (empty list)
Type bool
... Types char
So, that's one source of ambiguity. The reference in main() can match either template definition. Although there are various arcane rules that in some instances can resolve this kind of ambiguity, it's always better not to rely on arcane template disambiguation rules, or at least go no further than use several standard, basic SFINAE approaches.
But, back on topic, right off the bat the compiler is going to be behind an eight-ball here, not having a clear candidate to resolve the template reference in main(). But it gets worse. Given
auto func(Head arg, Args... args)
and
auto func(Head arg)
Because, once again, a variatic parameter, in this case it's Args..., can match an empty list, then, for example:
func(4)
Can match either function signature.
Combine all of these factors together, and the compiler can't really make heads or tails here.
I have the following class:
class FunctionCallback
{
public:
static CallbackHandle Create(const std::function<void(void)> &function);
template<typename T,typename... TARGS>
static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};
I then call 'Create' like this:
FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference
Even though that should be correct (?), visual studio underlines that line in read with the message:
Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
argument types are: (lambda []void (float f)->void)
However the program compiles fine in visual studio without any warnings or errors.
g++-5 is unable to compile it altogether with a similar message.
When changing it to:
FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));
It doesn't display the message anymore and compiles both on windows and linux.
Why can't it deduce the type properly unless I explicitly specify it?
You almost never want to deduce the type of a type-eraser, that std::function is an example of. With a few exceptions, template type deduction attempts to deduce the exact type of an argument. For a std::function<T>-type parameter this means that the compiler can deduce T if the corresponding argument is a std::function as well. The type of a lambda expression is not a std::function.
Even if you explicitly specify type template arguments, the deduction still continues after corresponding types have been substituted with the arguments. In the case of a parameter pack, the compiler still tries to deduce the rest of arguments, so for FunctionCallback::Create<void,float> the compiler ends up with:
template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);
and this won't match anything else than std::function<void(float, TARGS...)> (or something that derives from this type); otherwise the deduction for TARGS fails.
If your goal is to always specify the parameter pack's types explicitly, you can put that pack (or the entire parameter) in a non-deduced context:
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
struct FunctionCallback
{
template <typename T, typename... TARGS>
static void Create(identity_t<std::function<T(TARGS...)>> function);
};
FunctionCallback::Create<void, float>([](float f) -> void {});
DEMO
In this case, however, you'll have to pay the price of type-erasure. Instead, you can consider accepting any function object type:, letting the compiler to deduce the exact type of the argument:
struct FunctionCallback
{
template <typename F>
static void Create(F function){}
};
FunctionCallback::Create([](float f) -> void {});
DEMO 2