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.
Related
I've got the following code:
template <bool condition>
struct enable_if { };
template <>
struct enable_if<true> { using type = bool; };
template <typename T>
class is_callable {
using Yes = char[1];
using No = char[2];
template <typename U> static Yes& filter(decltype(&U::operator()));
template <typename U> static No& filter(...);
public:
constexpr operator bool() { return sizeof(filter<T>(nullptr)) == sizeof(Yes); }
};
template <typename Lambda, typename enable_if<is_callable<Lambda>{}>::type = true>
void doSomethingWithLambda(Lambda func) {
func();
}
int main() {
doSomethingWithLambda([]() { });
}
The important part is the enable_if<is_callable<Lambda>{}>::type part.
One is forced to instantiate is_callable<Lambda> with {} because if one were to use (), C++ would mistake it for a function call.
Feel free to correct me if I'm wrong, but as far as I know, C++ assumes it is a function in the () case so that the type of expression isn't determined after the time of writing, saving everyone a headache. What I mean by that is, assuming you had a function version and a class version of is_callable (separated by SFINAE using enable_if or something along those lines), the type Lambda could determine the true meaning of (), either a function call or an instantiation. Like I said, as far as I know, C++ wants to avoid this confusion, so it assumes function call and fails if such a function does not exist.
Based on the assumptions above, the following shouldn't work:
enable_if<(bool)is_callable<Lambda>()>::type
What does it matter if I cast the result of the function call (never mind that functions couldn't even be evaluated in this context)? Why is this suddenly treated as an instantiation instead of a function call?
No, your understanding is not correct.
Firstly, a name can't refer to both a class template and a function template. If that happens the program is ill-formed. (And defining both in the same scope is not allowed to begin with.)
Secondly, is_callable<Lambda>() as template argument is not a function call to begin with. It is a function type. It is the type of a function which has no parameters and returns a is_callable<Lambda>.
When the compiler parses a template argument, it can interpret it in two ways: Either as a type or as an expression (or as a braced-init-list), because template parameters can be type parameters or non-type parameters.
When the compiler reads is_callable<Lambda>() it notices that is_callable is a class template and then realizes that is_callable<Lambda> is therefore a type. If you have a type, let's shorten it to T, then T() can either be syntax representing the type of a function returning T and taking no arguments, or it can be an expression formed from one single functional notation explicit cast (which you imprecisely call "instantiation").
There is no way to differentiate these two cases in the context, but the compiler needs to know whether this is a type template argument or a non-type template argument. So there is a rule saying that such ambiguities are always resolved in favor of a type.
If is_callable was a function template instead, there would be no ambiguity, because then is_callable<Lambda> is not a type and therefore is_callable<Lambda>() cannot be a function type. It must be a function call instead and therefore an expression and non-type template argument.
When you write (bool)is_callable<Lambda>() this is not valid syntax for a type and therefore there is no ambiguity. It is a non-type template argument and an expression. And is_callable<Lambda>() is a funcational notation explicit cast because is_callable<Lambbda> is a type. If is_callable was a function template instead of a class template, then it would be a function call.
In looking at std::apply references from cpprefrence we can see that function templates cannot be passed as callable object of std::apply. Let's consider the following function template:
template<typename T>
T add_generic(T first, T second) { return first + second; }
So as the function template can't be deduced in std::apply call, we can't use the follwing code:
std::apply(add_generic, std::make_pair(2.0f, 3.0f)); // Error: can't deduce the function type
Please note that this is not the same question as this question. In that answer, the author writes a lambda expression without explicit template parameters.
std::cout << std::apply(
[](auto first, auto second) { return add_generic(first, second); },
std::make_tuple(2.0f,3.0f)) << '\n';
but as you know in c++20 you can have lambda expressions with explicit template parameter list. So I tried this feature to the case and surprisingly the compiler did not raise any errors.
std::apply([]<typename T>(T first,T second){
return first+second;
},std::make_pair(2.0,3.0));
Why will the compiler be able to deduce type in the last case? Is there any difference between the two?
A function template is not a function, just like a cookie cutter is not a cookie. C++ templates create new functions for every set of template arguments. And this is precisely why add_generic is not a viable argument. It's not a function, it cannot be passed around as a value. To obtain the function, the compiler needs to deduce the template arguments. But it can only do that inside apply after the callable has been passed. Chicken and egg right there.
Why does a lambda work? Because it's not a plain function either. It produces a hidden object type
struct __lambda_at_line_N {
template<typename T>
auto operator()(T first, T second) { /* ... */ }
};
And passes that
std::apply(__lambda_at_line_N{},std::make_pair(2.0,3.0));
This is an actual object, a value. Its type does not depend on the template argument deduction that needs to happen inside std::apply to call operator(). That's why it works.
I'm trying to write a function accepting a complex argument. Let's say it can be a class, or it can be a class template. The code inside the function is the same for all arguments, and I don't need to reference the argument's type anywhere. So, I want to write something like this:
auto func(auto& someComplicatedClassInstance) {
return someComplicatedClassInstance.someMemberFunc();
}
I can't just write template <typename T> because it won't work if T is a template itself, and auto function argument is not a thing in C++. Is making func a lambda the only way? It seems odd that the same compiler feature would be enabled for lambdas but not normal functions. And in my specific case, I'm writing an overload for operator<< so I can't make it a lambda.
You can use a function template:
template <typename T>
auto func(T&& someComplicatedClassInstance) {
return std::forward<T>(someComplicatedClassInstance).someMemberFunc();
}
Using auto in a lambda's parameter list is exactly the same as using a template parameter. It's simply a shorthand notation (in part because, until C++20, you cannot declare lambdas with regular template parameters otherwise). When the compiler generates the functor for the lambda, the operator() overload gets a template parameter for each auto parameter.
So for non-lambda functions, you have to write it out long-form.
I have following code:
void myfunc()
{
}
template <typename T>
void check()
{
}
template <typename T>
void checkT (T)
{
check<T>();
}
and so if I have in main function a call to checkT(myfunc) then that compiles, but if I have check<myfunc>() that doesn't work although it directly calls the first version. Can you please explain why it is so? The error is
error: no matching function for call to 'check()'
Thanks!
This is because myfunc is a value expression, not a type. You could do
check<decltype(myfunc)>();
though, or equivalently:
check<void(void)>();
See it live on http://liveworkspace.org/code/2ANEre$0
PS. In reply to the comment, I sense a bit of confusion between the function formal parameter and a template type parameter. Make it more explicit by writing:
template <typename T>
void checkT (T somevalue)
{
check<T>(); // note: somevalue is never used!
}
In the first instance checkT(myfunc) it is able to deduce the type, checkT is really identical to checkT( T value ) and so you are passing in value and T is being deduced. In the second case you are not supplying a type you can alter it like so to work:
check<decltype(myfunc)>() ;
you are actually supplying a value where you need a type in this case void(void).
checkT(myfunc) works because myfunc is a value. But the second fails because it is not a type, which the template parameters require. For example,
void checkT(T)
is the same as
void checkT(T t)
so that means the function passed is an object of type T. That is, t is the object and T is the type. In the template parameters of check, it requires an explicit specification of the type, not the object. So thus passing in an object would raise a compilation error. Just like passing in the number 5 where the explicit type int is expected.
You can use it as a type by wrapping it in a decltype expression:
check<decltype(myfunc)>();
// ^^^^^^^^^^^^^^^^
I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}