没有从 'lambda' 到 'void ...' 的可行转换
no viable conversion from 'lambda' to 'void ...'
我需要给一个函数另一个函数或 lambda 作为参数,这或多或少是可行的。
当我尝试在 C++14 中为 lambda 定义捕获时,就会出现错误。您可以在此处查看示例代码:
// this is part of a library (I cannot change it)
class SVGElement {
//...
public:
void onclick(void (*handler)(SVGElement *)) {
handler(this);
}
void rotateBy(int angle) {/*...*/}
//...
};
// my code
SVGElement mySvgElement = SVGElement();
// this works
mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(15);});
// as soon as I define a capture, there is an error
int angle = 15;
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
如您所见,部分问题是我无法更改部分代码。有什么我可以做的,或者我错过了什么,还是情况没有希望了?
这是我得到的错误:
input_line_7:22:22: error: no viable conversion from '(lambda at input_line_7:22:22)' to 'void (*)(__cling_N52::SVGElement *)'
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
input_line_7:7:25: note: passing argument to parameter 'handler' here
void onclick(void (*handler)(SVGElement *)) {
^
函数不能有捕获。 Lambda 可以,这意味着它们不是函数。没有捕获的 lambda 可以转换为函数指针,但有捕获的 lambda 不能。
此代码:
int angle = 15;
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
实际上等同于:
int angle = 15;
struct MyLambda {
int angle;
void operator()(SVGElement* clicked){clicked->rotateBy(angle);}
};
mySvgElement.onclick(MyLambda{angle});
并且无法将 MyLambda 对象视为函数指针,因为它不是函数,它实际上是一个包含变量的对象。
如果 lambda 没有 捕获,您可以很容易地构造一个包装函数,如下所示:
void MyLambda_wrapper(SVGElement* clicked) {
MyLambda l;
l(clicked);
}
然后你可以做
mySvgElement.onclick(MyLambda_wrapper);
这实际上就是编译器所做的。但是,这不适用于捕获,因为包装函数需要知道要放入捕获中的值。
Lambda 不允许您使用以前不能做的语言做任何新事情。它们只是做您已经可以做的事情的捷径。
不过,您确实有一些选项可以存储角度:
如果角度始终为 15,则可以硬编码 15。
如果所有形状的角度都相同,可以将其设为全局变量。
通常,库会在其数据结构中留下一些成员供应用程序使用,通常称为 void *context
或 void *userdata
。您可以将角度存储在该变量中:
int angle = 15;
mySvgElement.userdata = (void*)angle;
mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy((int)clicked->userdata);});
如果你有不止一个,你需要存储一个结构指针,并记得在元素被销毁时释放它:
mySvgElement.userdata = new my_svg_element_data;
((my_svg_element_data*)mySvgElement.userdata)->left_click_angle = 15;
((my_svg_element_data*)mySvgElement.userdata)->right_click_angle = 30;
mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(((my_svg_element_data*)clicked->userdata)->left_click_angle);});
mySvgElement.onrclick([](SVGElement* clicked){clicked->rotateBy(((my_svg_element_data*)clicked->userdata)->right_click_angle);});
mySvgElement.ondestroy([](SVGElement* destroyed){delete (my_svg_element_data*)destroyed->userdata;});
您可以将角度存储在您自己的全局中 std::unordered_map<SVGElement*, int> click_angle;
:
click_angle[&mySvgElement] = 15;
mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(click_angle[clicked]);});
mySvgElement.ondestroy([](SVGElement* destroyed){click_angle.erase(destroyed);});
我需要给一个函数另一个函数或 lambda 作为参数,这或多或少是可行的。 当我尝试在 C++14 中为 lambda 定义捕获时,就会出现错误。您可以在此处查看示例代码:
// this is part of a library (I cannot change it)
class SVGElement {
//...
public:
void onclick(void (*handler)(SVGElement *)) {
handler(this);
}
void rotateBy(int angle) {/*...*/}
//...
};
// my code
SVGElement mySvgElement = SVGElement();
// this works
mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(15);});
// as soon as I define a capture, there is an error
int angle = 15;
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
如您所见,部分问题是我无法更改部分代码。有什么我可以做的,或者我错过了什么,还是情况没有希望了?
这是我得到的错误:
input_line_7:22:22: error: no viable conversion from '(lambda at input_line_7:22:22)' to 'void (*)(__cling_N52::SVGElement *)'
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
input_line_7:7:25: note: passing argument to parameter 'handler' here
void onclick(void (*handler)(SVGElement *)) {
^
函数不能有捕获。 Lambda 可以,这意味着它们不是函数。没有捕获的 lambda 可以转换为函数指针,但有捕获的 lambda 不能。
此代码:
int angle = 15;
mySvgElement.onclick([angle](SVGElement* clicked){clicked->rotateBy(angle);});
实际上等同于:
int angle = 15;
struct MyLambda {
int angle;
void operator()(SVGElement* clicked){clicked->rotateBy(angle);}
};
mySvgElement.onclick(MyLambda{angle});
并且无法将 MyLambda 对象视为函数指针,因为它不是函数,它实际上是一个包含变量的对象。
如果 lambda 没有 捕获,您可以很容易地构造一个包装函数,如下所示:
void MyLambda_wrapper(SVGElement* clicked) {
MyLambda l;
l(clicked);
}
然后你可以做
mySvgElement.onclick(MyLambda_wrapper);
这实际上就是编译器所做的。但是,这不适用于捕获,因为包装函数需要知道要放入捕获中的值。
Lambda 不允许您使用以前不能做的语言做任何新事情。它们只是做您已经可以做的事情的捷径。
不过,您确实有一些选项可以存储角度:
如果角度始终为 15,则可以硬编码 15。
如果所有形状的角度都相同,可以将其设为全局变量。
通常,库会在其数据结构中留下一些成员供应用程序使用,通常称为
void *context
或void *userdata
。您可以将角度存储在该变量中:int angle = 15; mySvgElement.userdata = (void*)angle; mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy((int)clicked->userdata);});
如果你有不止一个,你需要存储一个结构指针,并记得在元素被销毁时释放它:
mySvgElement.userdata = new my_svg_element_data; ((my_svg_element_data*)mySvgElement.userdata)->left_click_angle = 15; ((my_svg_element_data*)mySvgElement.userdata)->right_click_angle = 30; mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(((my_svg_element_data*)clicked->userdata)->left_click_angle);}); mySvgElement.onrclick([](SVGElement* clicked){clicked->rotateBy(((my_svg_element_data*)clicked->userdata)->right_click_angle);}); mySvgElement.ondestroy([](SVGElement* destroyed){delete (my_svg_element_data*)destroyed->userdata;});
您可以将角度存储在您自己的全局中
std::unordered_map<SVGElement*, int> click_angle;
:click_angle[&mySvgElement] = 15; mySvgElement.onclick([](SVGElement* clicked){clicked->rotateBy(click_angle[clicked]);}); mySvgElement.ondestroy([](SVGElement* destroyed){click_angle.erase(destroyed);});