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)
Related
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);
};
In an attempt to rewrite a predicate combinator like this
auto constexpr all = [](auto const&... predicates){
return [predicates...](auto const&... x){
return (predicates(x...) && ...);
};
};
(little generalization of this) in a way that it would give meaningful errors when fed with non-predicates/predicates with different arities/arguments, I started writing something like this:
template<typename T, typename = void>
struct IsPredicate : public std::false_type {};
template<typename T>
struct IsPredicate<T, std::enable_if_t<std::is_same_v<bool, return_type_of_callable_T>, void>>
: public std::true_type {};
and then I stared at it for a while... How do I even check what is the return type of a function, if I don't even know how to call it?
I see this:
I couldn't even pass decltype(overloaded_predicate_function) to IsPredicate, because template type deduction can't occur with an overloaded name,
even if I only talk of function objects, the problem of the first bullet point could apply to operator(), in case it is overloaded.
So my question is: is it even possible to determine the return type of an arbitrary callable?
I'm mostly interested in a C++17 answer, but, why not?, I'd also like to know what C++20's concept offer in this respect.
So my question is: is it even possible to determine the return type of an arbitrary callable?
No. You can only do this in very narrow circumstances:
the callable is a pointer to member data / pointer to member function
the callable is a pointer/reference to function
the callable is a function object with a single non-overloaded function call operator that is not a template and no conversion functions to function pointers/reference
That's it. If you have a function object whose call operator is either overloaded or a template, you can't really figure out what its return type is. Its return type could depend on its parameter type, and you may not have a way of knowing what the parameter types could be. Maybe it's a call operator template that only accepts a few specific types that you have no way of knowing about, but it is a predicate for those types?
The best you can do is defer checking until you know what what arguments are. And then C++20 already has the concept for you (predicate):
inline constexpr auto all = []<typename... Ps>(Ps const&... predicates){
return [=]<typename... Xs>(Xs const&... x)
requires (std::predicate<Ps const&, Xs const&...> && ...)
{
return (std::invoke(predicates, x...) && ...);
};
};
Note that you should use std::invoke to allow for pointers to members as predicates as well (and this is what std::predicate checks for).
You cannot determine the return type of a callable without specifying the argument types (usually done by providing actual arguments) because the return type could depend on the argument types. "decltype(potential_predicate)" won't work but "decltype(potential_predicate(args...))" is another matter.
The first will be the type of the callable itself (whether pointer-to-function or a class type or whatever) while the second will produce the return type of the callable expression.
Can't give you a C++17 answer, but since you also asked for concepts:
The requires expression states that the () operator is overloaded and returns a bool. I think a predicate in the classical sense takes two arguments, but the concept can be easily extended to fo fulfill that requirement as well.
template<typename T>
concept IsPredicate =
requires(T a) {
{ a() } -> std::same_as<bool>;
};
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.
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.
So I have the following bit of code:
template <typename Type>
class Delegate
{
public:
Delegate(Type x)
{
}
};
void Method()
{
}
int main()
{
Delegate d(&Method);
return 0;
}
My question is: why can't the compiler deduce the template type based on what's passed into the constructor? The compile error I get is: Argument list for class template Delegate is missing. I understand that, but I thought type inference could overcome this to allow for cleaner syntax.
Because template parameter deduction only applies to functions. Class templates require parameters explicitly, always.
That's why many templates have a "named constructor" a function that simply constructs a temporary instance, but by virtue of being a function template rather than class template deduces parameters. For example std::make_pair.
C++11 introduced this new meaning of auto that actually allows you to deduce type of variable. So if you have C++11, you can create a "named constructor" for your class, like:
template <typename Type>
Delegate<Type> delegate(Type x) { return Delegate<Type>(x); }
and you can create a variable of deduced type with it like:
auto d = delegate(&Method);
Note, that this deduces d to be exactly the type returned by the initializer (you can have auto & or auto && if you want, but not much beyond that). This is way easier than trying to deduce hypothetical Delegate d(&Method), because that would involve cyclical dependency between deducing the type depending on overload resolution between constructors and the set of viable constructors depending on the deduced type (remember, constructors can be overloaded and types can be partially specialized).
It's for the same reason that this won't work:
// attempt to create a std::vector<std::string> of ten "x" strings:
std::vector v(10, "x");
In fact, it should result in the very same error message.
Use something like this to use type deduction:
template <class Type>
Delegate<Type> MakeDelegate(Type const &x)
{
return Delegate<Type>(x);
}
Or just do as you'd do with a std::vector and declare the type explictly.
By the way, main must return int, and arguments of unknown type (i.e. in templates) should be passed with const&.