Specializing a template on a lambda in C++0x
c++, c++11, lambda, specialization, templates
Solution
I think it is possible to specialize traits for lambdas and do pattern matching on the signature of the unnamed functor. Here is the code that works on g++ 4.5. Although it works, the pattern matching on lambda appears to be working contrary to the intuition. I've comments inline.
struct X
{
float operator () (float i) { return i*2; }
// If the following is enabled, program fails to compile
// mostly because of ambiguity reasons.
//double operator () (float i, double d) { return d*f; }
};
template <typename T>
struct function_traits // matches when T=X or T=lambda
// As expected, lambda creates a "unique, unnamed, non-union class type"
// so it matches here
{
// Here is what you are looking for. The type of the member operator()
// of the lambda is taken and mapped again on function_traits.
typedef typename function_traits<decltype(&T::operator())>::return_type return_type;
};
// matches for X::operator() but not of lambda::operator()
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
typedef R return_type;
};
// I initially thought the above defined member function specialization of
// the trait will match lambdas::operator() because a lambda is a functor.
// It does not, however. Instead, it matches the one below.
// I wonder why? implementation defined?
template <typename R, typename... A>
struct function_traits<R (*)(A...)> // matches for lambda::operator()
{
typedef R return_type;
};
template <typename F>
typename function_traits<F>::return_type
foo(F f)
{
return f(10);
}
template <typename F>
typename function_traits<F>::return_type
bar(F f)
{
return f(5.0f, 100, 0.34);
}
int f(int x) { return x + x; }
int main(void)
{
foo(f);
foo(X());
bar([](float f, int l, double d){ return f+l+d; });
}
Problem
I've written a traits class that lets me extract information about the arguments and type of a function or function object in C++0x (tested with gcc 4.5.0). The general case handles function objects: ``` template <typename F> struct function_traits { template <typename R, typename... A> struct _internal { }; template <typename R, typename... A> struct _internal<R (F::*)(A...)> { // ... }; typedef typename _internal<decltype(&F::operator())>::<<nested types go here>>; }; ``` Then I have a specialization for plain functions at global scope: ``` template <typename R, typename... A> struct function_traits<R (*)(A...)> { // ... }; ``` This works fine, I can pass a function into the template or a function object and it works properly: ``` template <typename F> void foo(F f) { typename function_traits<F>::whatever ...; } int f(int x) { ... } foo(f); ``` What if, instead of passing a function or function object into `foo`, I want to pass a lambda expression? ``` foo([](int x) { ... }); ``` The problem here is that neither specialization of `function_traits<>` applies. The C++0x draft says that the type of the expression is a "unique, unnamed, non-union class type". Demangling the result of calling `typeid(...).name()` on the expression gives me what appears to be gcc's internal naming convention for the lambda, `main::{lambda(int)#1}`, not something that syntactically represents a C++ typename. In short, is there anything I can put into the template here: ``` template <typename R, typename... A> struct function_traits<????> { ... } ``` that will allow this traits class to accept a lambda expression?