Разница в сигнатуре лямбда с / без захваченной переменной?
Я сейчас играю с C++11 и обнаружил следующую проблему с использованием лямбды в качестве обратного вызова для sqlite. При захвате векторной переменной внутри лямбды я получаю сообщение о том, что подписи не совпадают. Без использования этой переменной в лямбде ([]
вместо [&ret]
и не используя ret
внутри) работает нормально.
vector<SomeClass> ret;
char *err = nullptr;
int res = sqlite3_exec(db,
"some sql query, doesn't matter",
[&ret](void *unused, int argc, char **argv, char **columnName) -> int
{
ret.push_back(SomeClass());
return 0;
},
nullptr,
&err);
Это ошибка, которую я получаю:
cannot convert 'TestClass::testMethod()::<lambda(void*, int, char**, char**)>' to 'int (*)(void*, int, char**, char**)' for argument '3' to 'int sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), void*, char**)'
Версия GCC - "gcc (XvidVideo.RU - GCC 4.6.1 i686-pc-mingw32) 4.6.1 20110625 (предварительная версия)" для Windows.
Почему это имеет значение?
2 ответа
Только лямбды без захвата могут быть преобразованы в указатели для работы, и, на основе диагностики компилятора, ваш sqlite3_exec
ожидает такой указатель, int (*)(void*, int, char**, char**)
,
Цитировать §5.1.2[expr.prim.lambda]/6
Тип закрытия для лямбда-выражения без лямбда-захвата имеет открытую не виртуальную неявную функцию преобразования констант в указатель на функцию, имеющую тот же параметр и возвращаемые типы, что и оператор вызова функции типа закрытия.
Как насчет использования 1-го аргумента для обратного вызова?
vector<SomeClass> ret;
char *err = nullptr;
int res = sqlite3_exec(db,
"some sql query, doesn't matter",
[](void *ctx, int argc, char **argv, char **columnName) -> int
{
static_cast<vector<SomeClass>*>(ctx)->push_back(SomeClass());
return 0;
},
&ret,
&err);