Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specializing a template on a lambda in C++0x

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?

like image 242
Tony Allevato Avatar asked Apr 01 '10 17:04

Tony Allevato


People also ask

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

What is function template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

Is there lambda function in C?

Significance of Lambda Function in C/C++ Lambda Function − Lambda are functions is an inline function that doesn't require any implementation outside the scope of the main program. Lambda Functions can also be used as a value by the variable to store.


1 Answers

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; }); } 
like image 125
Sumant Avatar answered Sep 19 '22 12:09

Sumant