Reputation: 120731
It is possible to use the type of a lambda as a template argument, like
template<typename InArg, typename Function>
class selfCompose {
Function f;
public:
selfCompose(Function f): f(f) {}
auto operator() (InArg x) -> decltype(f(f(x))) {
return f(f(x)); }
};
int main() {
auto f = [](int x){return x*x;};
std::cout << selfCompose<int, decltype(f)>(f)(4) // yields (4²)² = 256
<< std::endl;
return 0;
}
However, this double use of f
is kind of redundant. We can omit passing the lambda's type as the template (casting it to a suitable std::function
(at loss of polymorphism – but C++ lambdas aren't parametrically polymorphic anyway)), however I have an application where I'd much prefer not having to pass its value to the constructor (because I'd like to use my class's initialisations themselves as a template parameter, where a particular constructor signature is expected). I'd like it to work like
template<class InArg, class Function>
class selfCompose {
Function f;
public:
selfCompose() {} // default constructor for f
auto operator() (InArg x) -> decltype(f(f(x))) {
return f(f(x)); }
};
int main() {
auto f = [](int x){return x*x;};
std::cout << selfCompose<int, decltype(f)>()(4) << std::endl;
return 0;
}
but this doesn't compile because lambdas have a deleted default constructor. Which is of course inevitable for capturing lambdas, but for simple ones like the one in my example this doesn't make much sense to me: they don't need to reference any local variables.
Is there some other way to get this functionality, or do I have to resort to defining the lambda old-fashionly as a named class?
struct myFun {
auto operator() (int x) -> int {return x*x;}
};
(of course, the lambda functions I'd like to use aren't quite as simple as x → x²
, so just selecting from a few standard function classes wouldn't be flexible enough)
Upvotes: 5
Views: 426
Reputation: 361612
Benjamin already posted a nice workaround for your case. You could use that.
My solution is just an improved version of Benjamin's answer where you don't have to specify the lambda parameter type. So instead of writing this:
make_selfCompose<int>(f)(4); //Benjamin's solution
you can write just this:
make_selfCompose(f)(4); //improved - absence `int` template argument.
and let make_selfCompose
deduce the lambda parameter type itself.
For this solution, lets write first function_traits
class template as:
#include <tuple>
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename C, typename R, typename... A>
struct function_traits<R(C::*)(A...) const>
{
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<A...>>::type type;
};
};
Then here is an improved version of make_selfCompose
:
template<typename Fun> //<--- now it has one template parameter
selfCompose<typename function_traits<Fun>::template arg<0>::type, Fun>
make_selfCompose(Fun f)
{
typedef typename function_traits<Fun>::template arg<0>::type InArg; //deduce it
return selfCompose<InArg, decltype(f)>(f);
}
Here is test program:
int main() {
auto f = [](int x){return x*x;};
std::cout << make_selfCompose(f)(4) //notice the relief here!
<< std::endl;
return 0;
}
See an online demo
Hope that helps. :-)
Upvotes: -2
Reputation: 35449
If selfCompose
is made polymorphic, there is no need to either explicitly pass the parameter type or to inspect the stored functor type. The latter means that it will also be able to deal with polymorphic functors.
template<typename Functor>
struct self_compose_type {
// Omitting encapsulation for brevity
Functor functor;
// It is possible to overload operator() to deal
// with all cv and ref qualifiers combinations
template<typename... T>
auto operator()(T&&... t)
// Don't use std::result_of for the return type
-> decltype( std::declval<Functor&>()(std::declval<T>()...) )
{ return functor(std::forward<T>(t)...); }
};
template<typename Functor>
self_compose_type<Functor>
self_compose(Functor&& functor)
{ return { std::forward<Functor>(functor) }; }
// C++03 style:
template<typename Functor>
self_compose_type<typename std::decay<Functor>::type>
make_self_compose(Functor&& functor)
{ return { std::forward<Functor>(functor) }; }
It is not necessary to make the operator()
variadic, you can make it accept exactly one argument if you so wish.
Upvotes: 1
Reputation: 103733
You can follow the example of functions like make_pair
and make_shared
:
template<typename InArg, typename Function>
selfCompose<InArg, Function> make_selfCompose(Function f)
{
return selfCompose<InArg, decltype(f)>(f);
}
int main() {
auto f = [](int x){return x*x;};
std::cout << make_selfCompose<int>(f)(4)
<< std::endl;
return 0;
}
Upvotes: 5