Suppose I have a lambda function:
std::function<void (int&& y)> lambda = [](int&& y) { std::cout << std::forward<int>(y) << std::endl; };
Having another function named gate which takes the lambda function as arg:
template<typename T> void gate(T&& x, std::function<void (T&&)> f) { f(std::move(x)); };
as the template argument deduction cannot make equal the types lambda(0) and std::function<void (T&&)>, Indeed, I specifically need to pass the std::function<void (T&&)> directly to through gate function, a commonly used solution is to make the lambda function parameter as non-deduced context. The scope is achieved using some struct which takes in some arbitrary type and spit it right back out. Hence, template argument deduction fails and the type of 'T&&' is deducted elsewhere, in this case from the first argument passed which type was correctly deducted.
template typename<T> struct Identity { typedef T type };
template<typename T> void gate(T&& x, typename Identity<std::function<void (T&&)>>::type f) { f(std::move(x)); };
int main() { gate(1234, [](int&& y) { std::cout << std::forward<int>(y) << std::endl; }); }
What I am wondering is, are there some lost in performance given the usage of the 'identity' struct? Could this be made better? Is creating firstly lambda and then pass it as argument the better way?
What I am wondering is, are there some lost in performance given the usage of the 'identity' struct?
Certainly no runtime performance because the structure is never instantiated or used.
Compilation speed could be affected since the compiler has to instantiate that type but it "amortizes" together with the instantiation of the function template itself since there is 1:1 correspondence. It will highly depend on the compiler how quickly it can throw those instantiations away.
FYI there is std::type_identity in C++20 for exactly this purpose which might allow the compiler to improve its performance. Maybe it will get similar treatment how Clang 15 now treats std::forward and others as builtins instead of instantiating them.
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);
};
If I do not use tempate parameter (type) in a function argument list -> only as return type, then there is no deduction:
template <typename T>
T zero() { return 0; }
int main()
{
int x = zero();
}
gives:
a.cpp:15:18: error: no matching function for call to ‘zero()’
int x = zero();
^
a.cpp:11:3: note: candidate: ‘template<class T> T zero()’
T zero() { return 0; }
^~~~
a.cpp:11:3: note: template argument deduction/substitution failed:
a.cpp:15:18: note: couldn't deduce template parameter ‘T’
int x = zero();
The only way to compile, is to specify the template type in angle brackets:
template <typename T>
T zero() { return 0; }
int main()
{
int x = zero<int>();
}
So my question is, why g++ can deduce the type from argument list of template function, but cannot deduce it from return type (which is also known for compiler when compiling main, so it knows the type).
providing the type in angle brackets for template function is then arbitrary (because of deduction), when the template function uses template types in its argument list? So as a good practise, should I always provided the type in curly braces, no matter how the function is declared?
The second question is not readable much. Put it in simple words -> should I use foo<T>(arg, ...) (provide the type) everytime, no matter the function declaration? Even if it can be deduced by the compiler, but I will provided the type anyway for good practise?
Generally it is not possible to deduce function based on its return type. But if you use automatic types conversion c++ feature then you could achieve what you need:
template <typename T>
T zero() { return 1; }
template <>
float zero<float>() { return 3.0f; }
struct Zero
{
template<typename T>
operator T()
{
return zero<T>();
}
};
int main()
{
int x = Zero();
float y = Zero();
return x + y;
}
First you create temporary object Zero(), and during assigment we use conversion operator to execute correct specialization of zero template function.
So my question is, why g++ can deduce the type from argument list of template function
GCC follows the rules set forth by the C++ standard.
should I use foo(arg, ...) (provide the type) everytime, no matter the function declaration?
That depends on what you want to achieve. If you want to be explicit, do so. That would be similar to calling a foo_T() function in C which does not have templates nor overloads. However, if you want your code to be generic (for instance, because it is called in a template itself or because you want it easier to change on future type changes), then you would prefer to avoid writing the type explicitly.
Another option is using overloading rather than a template. Which one you use, again, depends on what you want and your use case.
Finally, you can also use auto instead:
auto zero() { return 0; }
Having said that, for signatures/interfaces, I think the best is to use explicit types everywhere unless there is a reason not to (e.g. it needs to be a template):
int zero() { return 0; }
Question 1
While it might be relatively easy to modify rules in yout trivial cases, this is not the case generally.
Consider a case like that:
template <A, B, C> A f(B b, C c) { ... } // #1
int f(int a, int b) { ... } // #2
int f(int a, double b) { ... } // #3
And a call like that:
double x = f(1, 2.0); // Call #1 or #3?
Thus it is not always trivial to modify rules and ensure that existing code continue to works. Such changes could easily lead to ambiguities, silent change of function being called...
Standard usually avoid modifying language in a way that could make unexpected silent changes (or make legal code become ambiguous).
However, it could be nice if there was a way to specify that we want deduction for return type in specific cases. By using a contextual keyword, rules could be defined on how to handled cases like the above one. For example, it could be a conflict if using return type deduction prefer an overload that is not the same one as without return type deduction.
Question 2
No, you should not generally provide the type if not needed.
Alternatives
While the solution to returns an object of a class with conversion operators could works, in may cases, them simplest solution would be to change the return value for an output parameter.
template <class T> void zero(T &t) { t = 0; } // set_zero would be more readable
int x;
zero(x);
I would not consider that solution appropriate for that case as the following is much clearer:
auto x = zero<int>();
In practice, given that the default value is zero for numerical types and for most other types, then cannot be initialized from an integer or it might not have the intended result, it would be better to simply write:
int x = {};
or
int x {};
It wouldn't have been impossible to define language rules to satisfy this case, but it would have been non-trivial and completely pointless.
This function has no business being a template, when all the information needed to decide its template argument is in the function. It should just return int, period. If you don't want to spell out the return type for whatever reason, this is what auto is for: it'll perform the deduction you seek.
On the other hand, if you want the 0 to be converted to different types for you depending on the template argument, then that makes more sense, and you already have the solution to that: provide the template argument (how could the computer guess it?). In this particular example you'd be better off just converting at the callsite, but presumably you have some more complex logic in mind.
As for whether you should always give template arguments explicitly, even when they are deducible, I'd say this is to some degree a matter of style. On the other hand it seems pretty pointless and noisy, but on the other it can be self-documenting and ensure that you're invoking the specialisation that you think you're invoking. So I think that this is going to depend on context.
I suppose the basic premise of this question is that I'm trying to use enable_if along with Argument Dependent Lookup (ADL), but I'm not sure if it's possible. I do see on this page that
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.
So I imagine this won't work, but in the spirit of learning I'd like to put the question out there.
Here's an example of what I'm trying to get to happen:
#include <iostream>
namespace lib1 {
template <typename T>
void archive(T & t)
{
serialize(t);
}
}
namespace lib2 {
struct VectorInt {
int x;
int y;
};
struct VectorDouble {
double x;
double y;
};
template<typename T>
void serialize(std::enable_if<std::is_same<T, VectorInt>::value, T>::type & vect) {
std::cout << vect.x << std::endl;
}
// maybe do something different with VectorDouble. Overloading would work,
// but I'm curious if it can be made to work with enable_if
}
int main() {
lib2::VectorInt myvect;
myvect.x = 2;
lib1::archive(myvect);
}
The example is loosely based on something I'm trying to do with the cereal library. In my case I have several different types of vectors and matrices, and while I can use overloading to get functions to resolve properly, I was curious to use the enable_if feature to see if I could shorten the code.
Anyway, trying to compile that gives a message "error: variable or field 'serialize' declared void".
It's my understanding that this won't work because the enable_if is evaluated only after argument dependent lookup? Is that right?
For those that want to play around with this, I have the code up on repl.it: https://repl.it/repls/HalfBlandJumpthreading
There's two separate things going on in your example: there's (function) template argument deduction, and there's argument dependent lookup (ADL). The relationship between these two is slightly complex if you start trying to explicitly specify template parameters (hey, it's C++), you can read more here: http://en.cppreference.com/w/cpp/language/adl (in the notes section).
That said, in general in C++ it's typically better to allow function templates to deduce their arguments rather than specify them explicitly, which is what you were trying to do here anyway, so all is well.
When you do:
namespace lib1 {
template <typename T>
void archive(T & t)
{
serialize(t);
}
}
The call to serialize qualifies for ADL, and since it depends on t it is deferred until the template is instantiated since the type of t is required (this is called 2 phase lookup). When you call archive with an object of type VectorInt, the call to serialize will look in the namespace of VectorInt. Everything is working just fine. The problem is in this code:
template<typename T>
void serialize(std::enable_if<std::is_same<T, VectorInt>::value, T>::type & vect) {
std::cout << vect.x << std::endl;
}
You didn't specify template parameters explicitly, so they have to be deduced. But the form you give here does not allow deduction: http://en.cppreference.com/w/cpp/language/template_argument_deduction, see non-deduced contexts, the very first example. To try to understand better why, consider what you are asking the compiler to do: you are passing a VectorInt and asking the compiler to find T such that std::enable_if<std::is_same<T, VectorInt>::value, T>::type> happens to be VectorInt. This seems reasonable because intuitively enable_if is just the identity operator (for types) if the first argument is true. But the compiler has no special knowledge of enable_if. This is equivalent to saying: find T such that Foo<T>::type is Bar. The compiler has no way to do this shy of instantiating Foo for every single T, which is not possible.
We want to use enable_if, but not in a way that disables deduction. The best way to use enable_if is typically in a defaulted template parameter:
template<typename T, typename U = typename std::enable_if<std::is_same<T, VectorInt>::value>::type >
void serialize(T& vect) {
std::cout << vect.x << std::endl;
}
U isn't used for anything, but when serialize is passed a VectorInt, it will now deduce T from the passed argument, and then it will deduce U which has a default. However, if the enable_if argument is false then U will not correspond to any type, and the instantiation is ill-formed: classic SFINAE.
This answer is already pretty long, but enable_if itself is a reasonably deep topic; the form given above works here but doesn't work for disjoint sets of overloads. I'd suggest reading more about ADL, template argument deduction, SFINAE, and enable_if, outside of just SO (blog posts, Cppcon youtube videos, etc).
I've seen code like this:
#include <iostream>
// member_function_templates.cpp
struct X
{
template <class T> void mf(T* t) {
std::cout << "in mf t is: " << *t << std::endl;
}
};
int main()
{
int i = 5;
X* x = new X();
x->mf(&i); //why can this be called without specifying a type as in: x->mf<int>(&i) ??
}
My question is in the comment there in main. Why can you call:
x->mf(&i);
...without specifying a type? Intuition says it should be called like:
x->mf<int>(&i);
... but apparently does not have to be called like this (the above compiles with gcc 4.7 for me either way - with of without explicitly specifying the template.)
And, if you call it without specifying a type for the template, what will the type of T be (in the template function definition)? (I'm guessing it defaults to the type of whatever it is you pass into the function mf as an argument, but further explanation of how this works would be nice)
That is template type deduction and it applies to all template functions, not just members of a non-templated class. Basically the compiler is able to infer (following a set of rules in the standard) what the type T means from the arguments to the function. When the compiler sees the call to mf(&i);, it knows that i is an int, so the argument is an int* and that means that you want the overload that has T==int (*)
You can still provide a particular set of template arguments if you want to force a particular specialization of the template. If, for example, you want the parameter to the template not be the deduced one, but one that is convertible from it:
template<typename T>
void foo(T arg) {...}
int i = 10;
foo(i); // calls foo<int>(i)
foo<double>(i); // calls foo<double>(static_cast<double>(i))
(*) It is slightly more complicated than this, as there could be potentially multiple T types for which the argument would be valid, but the rules in the language determine what particular T will be deduced. In this case, it is int.
If we've this function template,
template<typename T>
void f(T param) {}
Then we can call it in the following ways,
int i=0;
f<int>(i);//T=int : no need to deduce T
f(i); //T=int : deduced T from the function argument!
//likewise
sample s;
f(s); //T=sample : deduced T from the function argument!
Now consider this variant of the above function template,
template<typename TArg, typename TBody>
void g(TArg param)
{
TBody v=param.member;
}
Now, can the compiler deduce the template arguments if we write,
sample s;
g(s); //TArg=sample, TBody=int??
Suppose sample is defined as,
struct sample
{
int member;
};
There are basically two questions:
Can the compiler deduce the template arguments in the second example?
If no, then why? Is there any difficulty? If the Standard doesn't say anything about "template argument deduction from function body", then is it because the argument(s) cannot be deduced? Or it didn't consider such deduction so as to avoid adding more complexity to the language? Or what?
I would like know your views on such deduction.
EDIT:
By the way, the GCC is able to deduce function arguments if we write this code:
template<typename T>
void h(T p)
{
cout << "g() " << p << endl;
return;
}
template<typename T>
void g(T p)
{
h(p.member); //if here GCC can deduce T for h(), then why not TBody in the previous example?
return;
}
Working demonstration for this example : http://www.ideone.com/cvXEA
Not working demonstration for the previous example: http://www.ideone.com/UX038
You probably already concluded that the compiler won't deduce TBody by examining the type of sample.member. This would add yet another level of complexity to the template deduction algorithm.
The template matching algorithm only considers function signatures, not their bodies. While not used too often, it's perfectly legal to simply declare a templated function without providing the body:
template <typename T> void f(T param);
This satisfies the compiler. In order to satisfy the linker, you must of course also define the function body somewhere, and ensure that all required instantiations have been provided. But the function body does not necessarily have to be visible to client code of the template function, as long as the required instantiations are available at link time. The body would have to explicitly instantiate the function, eg:
template <> void f(int param);
But this only partially applies to your questions, because you could imagine a scenario like the following, where a 2nd parameter could be deduced from the a provided default parameter, and which won't compile:
template<typename TArg, typename TBody>
void g(TArg param, TBody body = param.member); // won't deduce TBody from TArg
The template matching algorithm considers only the actual type, not any potential nested member types in case of classes or structs. This would have added another level of complexity which apparently was judged to be too complex. Where should the algorithm stop? Are members of members, and so forth, also to be considered?
Also, it's not required because there are other means of achieving the same intention, as shown in the example below.
Nothing prevents you from writing:
struct sample
{
typedef int MemberType;
MemberType member;
};
template<typename TArg>
void g(TArg param)
{
typename TArg::MemberType v = param.member;
}
sample s = { 0 };
g(s);
in order to obtain the same effect.
Regarding your sample you added after editing: whereas it seems that h(p.member) does depend on the member of the struct, and hence the template matching algorithm should fail, it doesn't because you made it a two-step process:
Upon seeing g(s);, the compiler looks for any function taking an argument of type sample (templated or not!). In your case, the best match is void g(T p). At this point, the compiler has not even looked at the body of g(T p) yet!.
Now, the compiler creates a instance of g(T p), specialized for T: sample. So when it sees h(p.member) it knows that p.member is of type int, and will try locate a function h() taking an argument of type int. Your template function h(T p) turns out to be the best match.
Note that if you had written (note the NOT_A_member):
template<typename T>
void g(T p)
{
h(p.NOT_A_member);
return;
}
then the compiler would still consider g() a valid match at stage 1. You then get an error when it turns out that sample does not have a member called NOT_A_member.
There are a couple of things that the compiler cannot possibly do with the code that you provide, the first of which is deduce the second template argument TBody. First, type deduction is only applied to the arguments of the function when the compiler is trying to match the call. At that point the definition of the templated function is not even looked at.
For extra credits, even if the compiler were to look the function definition, the code TBody v = parameter.member is non-deducible in itself, as there are potentially infinite data types that can accept a parameter.member in the constructor.
Now, on the second block of code. To understand it the whole process of template compilation starts when the compiler sees the function call g(x) at that point of call. The compiler sees that the best candidate is the template function template <typename T> void g( T ) and determines what the type T is as part of the overload resolution. Once the compiler determines that it is a call to the template, it performs the first pass of compilation on that function.
During the first pass, syntactic checks are perform without an actual substitution of the type, so the template argument T is still any-type and the argument p is of a yet-unknown type. During the first pass, the code is verified, but dependent names are skipped and their meaning is just assumed. When the compiler sees p.member, and p is of type T that is a template argument it assumes that it will be a member of the yet unknown type (this is the reason why if it is a type you would have to qualify it here with typename). The call h(p.member); is also dependent on the type argument T and is left as it is, assuming that once the type substitution takes place everything will make sense.
Then the compiler does actually substitute the type. At this step T is no longer a generic type, but it represents the concrete type sample. Now the compiler during the second pass tries to fill in the gaps that were left during the first pass. When it sees p.member it looks member inside the type and determines that it is an int and tries to resolve the call h( p.member ); with that knowledge. Because the type T has been resolved before this second stage, this is equivalent to the outside call g(x): all types are known and the compiler only needs to resolve what is the best overload for a function call h that takes an argument of type int&, and the whole process starts again, the template h is found as the best candidate, and ...
It is really important for metaprogramming to understand that type deduction is performed only on the actual signature of the function and not the body, and that is what makes it non-trivial for beginners. Using enable_if (from boost or elsewhere) in the function signature as argument or return type is not a coincidence, but the only way of having the compiler fail to substitute the type before the template is selected as best candidate and the substitution failure is turned into an actual error (and not SFINAE)
No compiler could possibly implement this functionality in a consistent manner. You are simply asking too much.
TBody might be ambiguous, because sample might not be the only type that has a member member. Furthermore, if g calls other template functions, the compiler has no way of knowing what other restrictions might be imposed on TBody.
So in some edge cases, it is theoretically possible to deduce the proper type for TBody, generally it is not.