Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to cast a lambda function to a function pointer?

I have this code:

void foo(void (*bar)()) {
    bar();
}

int main() {
    foo([] {
        int x = 2;
    });
}

However, I'm worried that this will suffer the same fate as:

struct X { int i; };

void foo(X* x) {
    x->i = 2;
}

int main() {
    foo(&X());
}

Which takes the address of a local variable.

Is the first example completely safe?

like image 932
Eric Avatar asked Mar 27 '13 10:03

Eric


2 Answers

A lambda that captures nothing is implicitly convertible to a function pointer with its same argument list and return type. Only capture-less lambdas can do this; if it captures anything, then they can't.

Unless you're using VS2010, which didn't implement that part of the standard, since it didn't exist yet when they were writing their compiler.

like image 193
Nicol Bolas Avatar answered Sep 22 '22 20:09

Nicol Bolas


Yes I believe the first example is safe, regardless of the life-time of all the temporaries created during the evaluation of the full-expression that involves the capture-less lambda-expression.

Per the working draft (n3485) 5.1.2 [expr.prim.lambda] p6

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

The above paragraph says nothing about the pointer-to-function's validity expiring after evaluation of the lambda-expression.

For e.g., I would expect the following to work:

auto L = []() {
   return [](int x, int y) { return x + y; };
};

int foo( int (*sum)(int, int) ) { return sum(3, 4); }


int main() {
  foo( L() );
}

While implementation details of clang are certainly not the final word on C++ (the standard is), if it makes you feel any better, the way this is implemented in clang is that when the lambda expression is parsed and semantically analyzed a closure-type for the lambda expression is invented, and a static function is added to the class with semantics similar to the function call operator of the lambda. So even though the life-time of the lambda object returned by 'L()' is over within the body of 'foo', the conversion to pointer-to-function returns the address of a static function that is still valid.

Consider the somewhat analagous case:

struct B {
   static int f(int, int) { return 0; }
   typedef int (*fp_t)(int, int);
   operator fp_t() const { return &f; }
};
int main() {
  int (*fp)(int, int) = B{};
  fp(3, 4); // You would expect this to be ok.
}

I am certainly not a core-c++ expert, but FWIW, this is my interpretation of the letter of the standard, and I feel it is defendable.

Hope this helps.

like image 38
Faisal Vali Avatar answered Sep 22 '22 20:09

Faisal Vali