Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template function causes a compiler error when used with local lambda

My previous question concluded that a distasteful "double cast" might be necessary to use the POSIX makecontext with a C++ lambda function (i.e. function object). Moving on, I'm now faced with a compilation error relating to the following minimal code:

#include <iostream>
#include <ucontext.h>

using namespace std;

template <typename T> void foo()   {
  ucontext_t c;
  auto f = [=](int i){ cout << i << endl; };
  makecontext(&c, (void (*) (void)) (void (*)(int)) f, 1, 12345);
}

int main(int argc, char *argv[]) {
  foo<int>();
  return 0;
}

The error is:

error: invalid cast from type ‘foo() [with T = int]::<lambda(int)>’ to type ‘void (*)(int)’

However, if I remove the unused (in this example) template argument from the foo function, so it becomes void foo();, and change the call to foo() the error disappears. Could someone tell me why? I'm using G++ 4.6.

Edit:

From the comments below, it seems the [=] in the code above causes the lambda to be a "capturing" lambda, regardless of the fact that it doesn't actually capture anything. The [=] is not necessary in my code, alas replacing with [] in GCC 4.6 does not remove the error. I am installing GCC 4.6.1 now...

like image 868
user2023370 Avatar asked Jul 27 '11 22:07

user2023370


2 Answers

If you use [=] to induce your lambda, you will not get a function pointer (or an object that is convertible to one). You will get a function object. And no amount of casting is going to allow you to pass that to makecontext. Not in any way that actually works.

According to N3291, the most recent working draft of C++0x:

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.

This is the only place where the specification allows conversion to a function pointer. Therefore, if recent versions of GCC do allow conversion to function pointers for [=], that not in accord with the specification.

like image 107
Nicol Bolas Avatar answered Nov 15 '22 07:11

Nicol Bolas


Only captureless lambdas are convertible to function pointers; while f technically does not capture anything, it does have a default capture mode of capturing by value (for no apparent reason).

Change [=] to [] in the declaration of f and it should work as expected.

EDIT: The fact that this compiles with more recent versions of GCC (as noted by Kerrek) gives a strong indication that this is merely a compiler bug in the version you're using.

like image 23
ildjarn Avatar answered Nov 15 '22 06:11

ildjarn