我有一个功能模板:
template <class ReportFunc>
void func (ReportFunc report_func)
{
for (/* ... */)
{
do_something (a, b);
report_func (a, b, c);
do_something_else (b, c);
}
}
有时需要在没有任何ReportFunc的情况下调用func(),即循环只调用do_something()和do_something_else()而不是其他内容。如果我写了一个没有采用ReportFunc参数的f()重载,我将不得不复制f()的实现代码,只需删除调用report_func()的行。
我有几种这样的功能 - 有时我想用ReportFunc调用它们,有时候没有它。所以我想避免所有的代码重复。如果我传递一个空的lambda或void或类似的东西,它是否应该使C ++ 11编译器生成f()的实例化,而不调用任何report_func()?它是否像删除调用report_func()的行一样快,甚至一个空的lambda都有一些开销,编译器没有优化? (在我的具体情况下,我使用GCC)
另外:如果一个空的lambda确实这样做了,并且我将函数f()的返回类型更改为ReportFunc,即它返回report_func参数,将返回的值存储在变量和调用中是否仍然是安全的它? (即使它是一个空的lambda?所以在理论上称它是可能的,它只是意味着什么都没有发生)
答案 0 :(得分:7)
只需传递一个空的仿函数。
只要你打开了优化,编译器就会实例化模板,内联对仿函数的(空)调用,所以什么都不做。它应该优化为零,不要打扰元编程来尝试删除调用。
我不会发誓G ++以同样的方式优化了“无所事事”的lambda,但是它应该这样做,因为类型是已知的并且它的函数调用运算符是内联的并且已知为空。
使用lambda没有固有的开销,它只是用于声明具有operator()
的对象类型并创建该类型的临时类型的语法糖。编译器前端需要做很多工作才能完成所有这些工作,但是一旦存在类型,优化器应该将它与用户定义的结构完全相同。因此,返回它也是安全的,它只是一个对象类型的实例,就像用户定义的函数对象一样。
答案 1 :(得分:1)
只要你的lambda没有通过引用捕获任何局部变量,就可以安全地返回并稍后调用(对于一个空的lambda,它只是一个没有成员变量的可调用对象,因此可以安全地复制和返回)
至于调用消除,由编译器决定lambda什么都不做并删除调用。
答案 2 :(得分:1)
您可以尝试这种方法,它是简单的实现,没有代码重复,并且减轻了在每个呼叫站点传递空lambda的痛苦:
struct EmptyParam
{
void operator()(int a, int b, int c){}
};
template <class ReportFunc>
void func (ReportFunc report_func)
{
int a = 0, b = 0, c = 0;
for (/* ... */)
{
do_something (a, b);
report_func (a, b, c);
do_something_else (b, c);
}
}
void func()
{
func<EmptyParam>(EmptyParam());
}
int _tmain(int argc, _TCHAR* argv[])
{
func([](int,int,int){});
func();
return 0;
}
编辑:为了完整性,以下是完全避免调用report_func的版本。对于你的特殊情况,它并不比我提出的第一个解决方案更优化,只是另一种做事方式。就个人而言,我会采用上述解决方案:
struct EmptyParam{};
template <class ReportFunc>
struct CallReportFunc
{
static void Call(const ReportFunc & report_func, int a, int b, int c)
{
report_func (a, b, c);
}
};
template <>
struct CallReportFunc<EmptyParam>
{
static void Call(const EmptyParam &/*report_func*/, int /*a*/, int /*b*/, int /*c*/)
{
// do nothing
}
};
template <class ReportFunc>
void func (ReportFunc report_func)
{
int a =0,b =0,c=0;
for (;true;)
{
CallReportFunc<ReportFunc>::Call(report_func, a, b, c);
}
}
void func()
{
func<EmptyParam>(EmptyParam());
}
int _tmain(int argc, _TCHAR* argv[])
{
func([](int,int,int){});
func();
return 0;
}