This question already has answers here:
Why doesn't C++11 implicitly convert lambdas to std::function objects?
(3 answers)
Closed 1 year ago.
In c++17, I have a template function which takes some kind of lambda as input. However it only recognizes those with explicit types and ones using auto are rejected.
Why this is the case and any way to combine auto variables and template function taking lambda with specified form as input?
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class C {};
vector<C> vec(2);
// ForEach func requires lambda must take Object with type C
// only, while its return type can vary as the function is templated.
template<typename T>
void ForEach(function<T (C a)>& lambda) {
std::for_each(begin(vec), end(vec), lambda);
};
int main()
{
auto
f_add_display4 = [](C i) {
};
std::function<void(C)>
f_add_display3 = f_add_display4;
ForEach(f_add_display3);
// ForEach(f_add_display4); // This line won't compile
}
A std function is not a lambda, and a lambda is not a std function.
Your function takes a std function. So when passed it, it deduces the template arguments to std function. If you pass a lambda, it cannot deduce anything.
Second, std function is a type erasure type, not an interface. Your function attempts to deduce the template argument of the type erasure class. Doing this is an anti pattern.
template<class F>
void ForEach(F lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
this works.
If you want to restruct the F to accepting some signature, in c++20 you can do:
void ForEach(std::invocable<C> auto lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
Or in c++17:
template<std::invocable<C> F>
void ForEach(F lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
There are actually two fundamental issues here which cause the deduction to fail:
The first is that the type of a lambda expression is never std::function<Signature> for any signature Signature. However, your function expects a non-const reference argument. As the types differ a conversion is needed which would be a temporary object and temporary objects never bind to non-const reference. You could fix this issue by using a const reference as argument:
template <typename T>
void ForEach(function<T(C)> const& lambda) { ... }
The other problem is that ForEach takes a conceptually open-ended set of potential arguments. However, the argument you have isn't an immediate match: there is no way to to deduce the type T based on the lambda type to exactly match the function argument. Instead, a conversion is required. The compiler won't try to find what instantion might work as the target of an instantiation, although in this case there is only one choice. The conversion itself would work if you'd specify the target type (and made the previous choice of making the parameter const&:
ForEach<void>(f_add_display4);
I would recommend to not constraint the function to take a function<T(C)> to start with! Compared to using an actual lambda function is most likely a pessimization: while the lambda function is statically typed and can, in general, be optimized well, the same is not true for std::function<Signaure>. While the latter can sometimes be optimized often it isn't. If you want to constraint the function to only accept parameters of type C you can do that with some other approaches, probably involving SFINAE for C++17 compilers or a concept for C++20+.
That is, I'd recommend using
template <typename Fun>
void ForEach(Fun&& lambda) {
...
}
... of, if you want to constrain the function using C++20 concepts
template <typename Fun>
requires requires (Fun lambda, C c) { lambda(c); }
void ForEach(Fun&& lambda) {
...
}
The type of f_add_display3 is not std::function<void(C)>. It is of some unnamed class type (some class with a void operator()(C i)).
You can convert the lamda to a std::function<void(C)> as you do here:
std::function<void(C)> f_add_display3 = f_add_display4;
Though, template argument deduction does not consider implicit conversions.
If you change the argument to be a const reference, then you can specify the template argument explicitly:
template<typename T>
void ForEach(const function<T (C a)>& lambda) {
// ...
};
ForEach<void>(f_add_display4); // This line will compile !
Or you drop the unnecessary conversion to std::function entirely:
template <typename F>
void ForEach(F f) {
std::for_each(begin(vec), end(vec),f);
};
Related
This doesn't work with either gcc-10 or clang-10.
template <typename R, typename T>
auto invoke_function(R (&f)(T), T t) { return std::invoke(f, t); }
invoke_function(std::to_string, 42);
This works with gcc-10, but not clang-10.
template <typename R, typename T>
auto invoke_function(T t, R (&f)(T)) { return std::invoke(f, t); }
invoke_function(42, std::to_string);
Error messages are very similar in all cases: "couldn't infer template argument 'R'" or "couldn’t deduce template parameter ‘R’" (gcc).
It isn't clear why this code is rejected. Since T is deduced, the overload of std::to_string can be determined. The dependency on argument order is particularly annoying. Shouldn't it Just Work?
I know this problem can be sidestepped by introducing a function object:
struct to_string
{
template<typename T> std::string operator()(T t) { return std::to_string(t); }
};
and then just using std::invoke on it. This however requires creating a separate function object for each overload set.
Is there a better way?
It isn't clear why this code is rejected. Since T is deduced, the overload of std::to_string can be determined.
That's not how it works exactly. Template deduction deduces each parameter/argument pair independently first - and then we bring all the deductions together and ensure that they're consistent. So we deduce T from 42 and then, separately, we deduce R(&)(T) from std::to_string. But every overload of std::to_string matches that pattern, so we don't know which one to pick.
But the above is only true if we can deduce each pair independently. If a parameter is non-deducible, we skip it and then try to go back and fill it in later. And that's the key here - we restructure the deduction such that we only deduce T from 42:
template <typename T>
auto invoke_function(std::string (&f)(std::type_identity_t<T>), T t) { return std::invoke(f, t); }
Here, we deduce T and int and now we're deducing std::string(&)(int) from std::to_string. Which now works, because only a single overload matches that pattern.
Except now this is undefined behavior, as per [namespace.std]/6:
Let F denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template. Unless F is designated an addressable function, the behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to F.
std::to_string is not an addressable function.
So the real better way is to just wrap to_string in a lambda and pass that along:
invoke_function([](auto x){ return std::to_string(x); }, 42);
And just adjusting invoke_function to take an arbitrary callable rather than specifically a function. That lambda wrapping generalizes to:
#define FWD(x) static_cast<decltype(x)&&>(x)
#define LIFT(name) [&](auto&&... args) noexcept(noexcept(name(FWD(args)...))) -> decltype(name(FWD(args)...)) { return name(FWD(args)...); }
invoke_function(LIFT(std::to_string), 42);
Given the following templated function:
template <typename T>
void f(std::function <void (T)>) {}
Can I extract T without having to explicitly mention it when calling f?
f<int>([](int){}); works fine but I'd like T to be deduced and f([](int){}); to just work. The latter errors out with "no matching function for call to".
You can deduce T if the object passed into the function actually has type std::function<void(T)>. If the type you pass in is a lambda, then you'll have to deduce it from the type of the lambda's function call operator, like so:
template <class Callable, class Arg, class T>
void f_helper(Callable callable, Ret (Callable::*)(T)) {
// do something with T
}
template <class Callable>
void f(Callable callable) {
f_helper(callable, &callable::operator());
}
Actually, in reality it is a bit more annoying than this, because you need at least two overloads for f_helper, according to whether the lambda is declared mutable (which determines whether operator() is const), and in C++17 you also need to double the number of overloads again according to whether the lambda is noexcept.
The standard library sidesteps issues like this by not attempting to extract the argument type from the callables you pass to algorithms such as std::sort. It just accepts an arbitrary callable type and then tries to call it.
I am currently working on a library where I am chaining function objects.
I am creating a function template that takes a callable object (std::function at the moment) and is parametrized on the output and input type of the function. Here is a simplified version of what I am defining:
template <typename In, typename Out>
std::vector<Out> process(std::vector<In> vals, std::function< Out(In) > func)
{
// apply func for each value in vals
return result;
}
The problem I am having is on usage. It seems that when I pass a lambda, the compiler cannot deduce the type correctly, so complains that the function doesn't exist:
std::vector<string> strings;
// does NOT compile
auto chars = process(strings,
[]( std::string s ) -> char
{
return s[0]; // return first char
}
);
If I explicitly wrap the lambda in std::function, the program compiles:
std::vector<string> strings;
// DOES compile
auto chars = process(strings,
std::function< char(std::string) >(
[]( std::string s ) -> char
{
return s[0]; // return first char
})
);
I haven't tested passing function pointers or function objects yet, but it seems like it will be difficult for the compiler to deduce the the In and Out parameters if I'm not directly passing the explicit std::function object.
My question is this: is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
Perhaps parametrize the template on the function type instead of the input/return types? Essentially I need to deduce the In and Out type for an arbitrary callable. Perhaps some kind of auto/decltype trick for the return type of the template function?
Thank you.
I think what you can do is to create an intermediate return type deducing function which uses decltype to determine the arguments to be passed to the actual function object:
template <typename Out, typename In>
std::vector<Out> process_intern(std::vector<In> vals, std::function< Out(In) > func)
{
// whatever
}
template <typename In, typename Func>
auto process(std::vector<In> vals, Func func) -> std::vector<decltype(func(vals[0]))>
{
return process_intern<decltype(func(vals[0]))>(vals, func);
}
Of course, you may want to consider implementing the logic in process() directly anyway unless there is a reason to type erase the function type.
is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
No. User-defined conversions are not considered in template argument deduction. The compiler have to come up with In and Out such that the type of the parameter and the type of the argument have to match (almost) exactly, but they never will in this case.
Perhaps parametrize the template on the function type instead of the input/return types
Yes, that's what is normally done (take a look at the standard library algorithms, for example)
The task is to create a single-argument function that forwards all types apart from one (Foo), which it converts (to Bar).
(Let us assume there exists a conversion from Foo to Bar).
Here is the usage scenario:
template<typename Args...>
void f( Args... args )
{
g( process<Args>(args)... );
}
(I've tried to extract/simplify it from the original context here. -- if I've made a mistake, please someone tell me!)
Here are two possible implementations:
template<typename T>
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) {
return Bar{x};
}
And...
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
I have it on good authority (here) that the second one is preferable.
However, I can't understand the given explanation. I think this is delving into some of the darkest corners of C++.
I think I'm missing machinery necessary to understand what's going on. Could someone explain in detail? If it is too much digging, could anyone recommend a resource for learning the necessary prerequisite concepts?
EDIT: I would like to add that in my particular case the function signature is going to match one of the typedef-s on this page. That is to say, every argument is going to be either PyObject* (with PyObject being an ordinary C struct) or some basic C type like const char*, int, float. So my guess is that the lightweight implementation may be most appropriate (I'm not a fan of over-generalising). But I am really interested in acquiring the right mindset to solve such problems as these.
I sense a minor misconception in your understanding of the use case you are facing.
First of all, this is a function template:
struct A
{
template <typename... Args>
void f(Args... args)
{
}
};
And this is not a function template:
template <typename... Args>
struct A
{
void f(Args... args)
{
}
};
In the former definition (with a function template) the argument type deduction takes place. In the latter, there is no type deduction.
You aren't using a function template. You're using a non-template member function from a class template, and for this particular member function its signature is fixed.
By defining your trap class like below:
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args);
};
and referring to its member function like below:
&trap<decltype(&Base::target), &Base::target>::call;
you end up with a pointer to a static non-template call function with a fixed signature, identical to the signature of the target function.
Now, that call function serves as an intermediate invoker. You will be calling the call function, and that function will call the target member function, passing its own arguments to initialize target's parameters, say:
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args)
{
return (get_base()->*t)(args...);
}
};
Suppose the target function used to instantiate the trap class template is defined as follows:
struct Base
{
int target(Noisy& a, Noisy b);
};
By instantiating the trap class you end up with the following call function:
// what the compiler *sees*
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, b);
}
Luckily, a is passed by reference, it is just forwarded and bound by the same kind of reference in the target's parameter. Unfortunately, this doesn't hold for the b object - no matter if the Noisy class is movable or not, you're making multiple copies of the b instance, since that one is passed by value:
the first one: when the call function is invoked itself from an external context.
the second one: to copy b instance when calling the target function from the body of call.
DEMO 1
This is somewhat inefficient: you could have saved at least one copy-constructor call, turning it into a move-constructor call if only you could turn the b instance into an xvalue:
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, std::move(b));
// ~~~~~~~~~~~^
}
Now it would call a move constructor instead for the second parameter.
So far so good, but that was done manually (std::move added knowing that it's safe to apply the move semantics). Now, the question is, how could the same functionality be applied when operating on a parameter pack?:
return get_base()->target(std::move(args)...); // WRONG!
You can't apply std::move call to each and every argument within the parameter pack. This would probably cause compiler errors if applied equally to all arguments.
DEMO 2
Fortunately, even though Args... is not a forwarding-reference, the std::forward helper function can be used instead. That is, depending on what the <T> type is in std::forward<T> (an lvalue reference or a non-lvalue-reference) the std::forward will behave differently:
for lvalue references (e.g. if T is Noisy&): the value category of the expression remains an lvalue (i.e. Noisy&).
for non-lvalue-references (e.g. if T is Noisy&& or a plain Noisy): the value category of the expression becomes an xvalue (i.e. Noisy&&).
Having that said, by defining the target function like below:
static R call(Args... args)
{
return (get_base()->*t)(std::forward<Args>(args)...);
}
you end up with:
static int call(Noisy& a, Noisy b)
{
// what the compiler *sees*
return get_base()->target(std::forward<Noisy&>(a), std::forward<Noisy>(b));
}
turning the value category of the expression involving b into an xvalue of b, which is Noisy&&. This lets the compiler pick the move constructor to initialize the second parameter of the target function, leaving a intact.
DEMO 3 (compare the output with DEMO 1)
Basically, this is what the std::forward is for. Usually, std::forward is used with a forwarding-reference, where T holds the type deduced according to the rules of type deduction for forwarding references. Note that it always requires from you to pass over the <T> part explicitly, since it will apply a different behavior depending on that type (not depending on the value category of its argument). Without the explicit type template argument <T>, std::forward would always deduce lvalue references for arguments referred to through their names (like when expanding the parameter pack).
Now, you wanted to additionally convert some of the arguments from one type to another, while forwarding all others. If you don't care about the trick with std::forwarding arguments from the parameter pack, and it's fine to always call a copy-constructor, then your version is OK:
template <typename T> // transparent function
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) { // overload for specific type of arguments
return Bar{x};
}
//...
get_base()->target(process(args)...);
DEMO 4
However, if you want to avoid the copy of that Noisy argument in the demo, you need to somehow combine std::forward call with the process call, and pass over the Args types, so that std::forward could apply proper behavior (turning into xvalues or not doing anything). I just gave you a simple example of how this could be implemented:
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
//...
get_base()->target(process<Args>(args)...);
But this is just one of the options. It can be simplified, rewritten, or reordered, so that std::forward is called before you call the process function (your version):
get_base()->target(process(std::forward<Args>(args))...);
DEMO 5 (compare the output with DEMO 4)
And it will work fine as well (that is, with your version). So the point is, the additional std::forward is just to optimize your code a little, and the provided idea was just one of the possible implementations of that functionality (as you can see, it brings about the same effect).
Would not be the first part of Version 2 sufficient? only:
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
Given a usage case with an existing conversion (constructor for "Bar" from "Foo"), like:
struct Foo {
int x;
};
struct Bar {
int y;
Bar(Foo f) {
y = f.x;
}
};
int main() {
auto b = process<Bar>(Foo()); // b will become a "Bar"
auto i = process<int>(1.5f);
}
You are forced to specify the first template parameter (the type to convert to) anyway because the compiler cannot deduce it. So it knows what type you expect and will construct a temporary object of type "Bar" because there is a constructor.
What are the differences between
template <typename T> void func( T t ) { /* ... */ }
and the C++14 alternative using lambdas with auto parameters?
auto func = []( auto t ) { /* ... */ }
Which one should be preferred?
The first is a function template. It can be specialized and overloaded. It can be found by ADL. When you want to take the address, you must either explicitly give it template parameters or do it in a context where the compiler can deduce them.
The second, assuming it appears on namespace scope, is a global object with a templated function call operator. It cannot be specialized or overloaded (global variables conflict with functions, they don't overload them). It cannot be found by ADL (ADL only finds functions and function templates). If you use the address operator on it, you get the address of the object, which is pretty useless. The object itself can be converted to a function pointer if the compiler can deduce the arguments; you cannot supply them explicitly.
You can use whichever you want; just be aware of the advantages and disadvantages of either choice. I would recommend the first. The only advantage of the second is its terseness, and I hope we'll get terse syntax for function templates in the not-too-distant future as well.
auto func(auto t) { ... }
The difference is that the first one is function template which you have to define before you use it; once the definition is there, anyone can use it. So it is a reusable piece of code and remains there forever.
Lambdas, on the other hand, are handy: you can define it when you need it. If the lambda is defined inside a function, as a local object, then only that function can use it and pass it to other functions. It is still reusuable, but less than function template. However lambdas, defined at namespace level, is as reusable as function template, because anyone can use it. So it is not much different from function template when you define it at namespace level. There can be some corner cases to be discovered by experts. One such case is, you can specialize function template:
//specialization : possible only in case of template!
template<> void func(MyClass obj) { /* ... */ }
You cannot do this with lambdas!
N3337, [expr.prim.lambda]/3:
The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed nonunion class type — called the
closure type — whose properties are described below. This class type
is not an aggregate (8.5.1). The closure type is declared in the
smallest block scope, class scope, or namespace scope that contains
the corresponding lambda-expression.
This closure type will stay a class. But its overloaded function call operator will be a operator function template, allowing different specializations. Furthermore, unlike function templates, you can implicitely convert a closure object to a function pointer. Its really handy, isn't it?
Quoting N3559, it'll look something like that:
For a generic lambda L:
int(*fp)(int, char) = [](auto a, auto b){return a+b;};
The closure type is
struct/*anonymous*/
{
template<class A,class B>
auto operator()(A a,B b) const
{
return a+b;
}
private:
template<class A,class B>
static auto __invoke(A a,B b)
{
return a+b;
}
template<class A,class B,class R>
using fptr_t = R(*)(A,B);
public:
template<class A,class B,class R>
operator fptr_t<R,A,B>() const
{
return &__invoke<A,B>; // Fixed that manually, they forgot to qualify the template name
}
} L;
int(*fp)(int,char) = L;
(There will be usual template argument deduction performed)