12

I have a question regarding C++0x lambdas. In my code, it would be beneficial to know whether or not a given type is the type of a C++0x lambda expression. To give an example:

struct foobar
{
  void operator()()
  {
  }
};

auto lambda = []{};
typedef is_lambda < decltype(lambda) > ::type T; // T would be a true_type
typedef is_lambda < foobar > ::type T; // T would be a false_type

It is rather easy to distinguish lambda expressions from function and member function types. Functors are another matter.

The problem I see here is the definition of lambda expressions according to the upcoming C++0x standard; the only thing that must be defined is a public call operator. However, this is true for a functor as well; testing for the presence of the call operator is not enough for distinguishing lambda expressions from functors. Furthermore, if the operator of a functor is not present, a compiler error will occur, since SFINAE does not apply. When does this happen? The functor's call operator may be templated. So, such a code:

typedef decltype(&T::operator()) call_type;

will work for both lambda expressions and functors with non-templated call operator, and generate a compiler error for templated call operators.

I believe an is_lambda < > trait can only be created using intrinsic compiler features. Do you see a way how to implement this trait?

Motti
  • 110,860
  • 49
  • 189
  • 262
dv_
  • 1,247
  • 10
  • 13
  • 3
    I wonder what would you use it for? – Maxim Egorushkin Jan 11 '11 at 20:36
  • Sorry for the late reply. Yes, I think I made a logical error. There is no point in distinguishing regular functors from lambdas - I can view the latter as the former. However, there is need for determining whether or not a call operator exists. To this date, no fully generic solution for this problem seems to exist. I will address this in a separate question soon, along with my attempts. – dv_ Jan 24 '11 at 08:47
  • @MaximYegorushkin: As for a motivating difference: the type of a *closure object* uniquely identifies it implementation. The same is not (necessarily) true for other function pointers or other function like objects. – BCS Mar 16 '14 at 21:40

4 Answers4

8

Since evaluation of lambda results in creating closure object, there isn't any difference as soon as the object passed to a function or copied. And, frankly, I can't imagine a problem that would require to know whether an object came from lambda.

Edit. A standard even has a note in 5.1.2/2:

Note: a closure object behaves like a function object (20.8).—end note

Motti
  • 110,860
  • 49
  • 189
  • 262
Gene Bushuyev
  • 5,512
  • 20
  • 19
  • I do imagine that you could be willing to know whether a `std::function<...>` type is stateful or not. However since functions are allowed to use `static` or global variables, their would not be much point in distinguishing lambdas from the mix. – Matthieu M. Jan 12 '11 at 07:20
6

I don't believe that it can be done- lambdas aren't really anything new semantically, they're just compiler-generated functors and thus will look identical to regular functors.

Puppy
  • 144,682
  • 38
  • 256
  • 465
2

It's possible to define some macro code that determines if an expression is a lambda expression (but that's not very useful as it doesn't tell you if an expression is of a lambda type).

#include <type_traits>

template<typename T, typename U>
struct SameType {
    static_assert(!std::is_same<T, U>::value, "Must use Lambda");
    static T pass(T t) { return t; }
};

template <typename T, typename U>
T NotLambda(T t, U u) { return SameType<T, U>::pass(t); }

#define ASSERT_LAMBDA(x) NotLambda(x,x)

/////////////////////////////////////

int fn() { return 0; }

int main() {
    auto l = []{ return 0; };
    return ASSERT_LAMBDA(fn)() +             // << fails
           ASSERT_LAMBDA(l)() +              // << fails
           ASSERT_LAMBDA([]{ return 0; })(); // << passes
}

This depends on section 5.1.2.3 which specifies that each lambda expression has a distinct type (which I think is a property unique to lambdas).

BCS
  • 75,627
  • 68
  • 187
  • 294
  • I had the same idea. If only it could be done without the ugly macro, but while I find the syntax of using `NotLambda` directly (with a lambda expression twice), to be tolerable, a user could of course accidentally provide two distinct function objects. – user2023370 Jun 11 '14 at 12:11
1

I created a header-only compiler specific (msvc >= 19.20?, gcc >= 7.3, clang >= 6.0) is_lambda type trait in c++17 if anyone interested in.

https://github.com/schaumb/is_lambda-cpp-type-trait

This can be used as in the question:

struct foobar
{
  void operator()()
  {
  }
};

auto lambda = []{};
typedef bxlx::is_lambda < decltype(lambda) > T; // T is true_type
typedef bxlx::is_lambda < foobar > U; // U is false_type

There are more examples for the usage.


  • 1
    TL;DR: the idea is to use [`__PRETTY_FUNCTION__` trick](https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c) to determine the name of the type, and check if looks like `"lambda at foo.cpp"`. – HolyBlackCat Jan 14 '20 at 15:32