C++ C++;11优化作为回调传递的空函数
我有一个函数模板:C++ C++;11优化作为回调传递的空函数,c++,templates,c++11,lambda,callback,C++,Templates,C++11,Lambda,Callback,我有一个函数模板: template <class ReportFunc> void func (ReportFunc report_func) { for (/* ... */) { do_something (a, b); report_func (a, b, c); do_something_else (b, c); } } 模板 无效函数(ReportFunc report_func) { 对于(
template <class ReportFunc>
void func (ReportFunc report_func)
{
for (/* ... */)
{
do_something (a, b);
report_func (a, b, c);
do_something_else (b, c);
}
}
模板
无效函数(ReportFunc report_func)
{
对于(/*…*/)
{
做某事(a,b);
报告(a、b、c);
做点别的(b,c);
}
}
有时需要在不使用任何ReportFunc的情况下调用func(),即循环只调用do_something()和do_something_else()而不调用其他任何东西。如果我编写的f()重载不带ReportFunc参数,我将不得不复制f()的实现代码,只需删除调用report_func()的行
我有几种这类函数——有时我想用ReportFunc调用它们,有时不使用它。所以我想避免所有的代码重复。如果我传递一个空lambda或void或类似的东西,它是否应该让C++11编译器生成一个不调用任何report_func()的f()实例化?它是否像简单地删除调用report_func()的行一样快,甚至是一个空lambda也有一些编译器没有优化的开销?(在我的具体案例中,我使用GCC)
另外:如果空lambda确实这样做,并且我将函数f()的返回类型更改为ReportFunc,即它返回report_func参数,那么将返回值存储在变量中并调用它是否仍然安全?(即使它是一个空的lambda?因此调用它在理论上是可能的,它只是意味着什么都不会发生)只要您的lambda没有通过引用捕获任何局部变量,就可以安全地返回它并稍后调用(对于空的lambda,它只是一个没有成员变量的可调用对象,因此可以安全地复制和返回)
至于调用消除,由编译器确定lambda不做任何操作并删除调用。您可以尝试这种方法,它实现简单,无代码重复,并且减轻了在每个调用站点传递空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;
}
struct EmptyParam
{
void运算符()(inta,intb,intc){}
};
模板
无效函数(ReportFunc report_func)
{
int a=0,b=0,c=0;
对于(/*…*/)
{
做某事(a,b);
报告(a、b、c);
做点别的(b,c);
}
}
void func()
{
func(EmptyParam());
}
int _tmain(int argc,_TCHAR*argv[]
{
func([](int,int,int){});
func();
返回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;
}
struct EmptyParam{};
模板
结构CallReportFunc
{
静态无效调用(常量ReportFunc&report_func,int a,int b,int c)
{
报告(a、b、c);
}
};
模板
结构CallReportFunc
{
静态void调用(const EmptyParam&/*report_func*/,int/*a*/,int/*b*/,int/*c*/)
{
//无所事事
}
};
模板
无效函数(ReportFunc report_func)
{
int a=0,b=0,c=0;
因为(;真;)
{
CallReportFunc::Call(report_func,a,b,c);
}
}
void func()
{
func(EmptyParam());
}
int _tmain(int argc,_TCHAR*argv[]
{
func([](int,int,int){});
func();
返回0;
}
只需传递一个空函子
只要打开了优化,编译器就会实例化模板,内联(空)对函子的调用,因此什么也不做。它应该优化到零,不必费心元编程来尝试删除调用
我不会发誓G++会以同样的方式优化掉一个“不做任何事情”的lambda,但它应该这样做,因为类型是已知的,它的函数调用操作符是内联的,并且已知为空
使用lambda没有固有的开销,它只是用
操作符()声明对象类型并创建该类型的临时对象的语法糖。编译器前端需要做大量的工作来完成所有这些,但是一旦类型存在,优化器就应该将其视为执行相同操作的用户定义结构。出于这个原因,返回它也是安全的,它只是一个对象类型的实例,就像用户定义的函数对象一样。将一个带有空内联操作符()的函数对象传递给它。
。一个普通的空内联函数也应该可以工作。@n.m.如果我传递一个空lambda,它会一样快吗?我这样问是因为它使代码看起来更好:lambda是在代码中使用它的地方定义的,而不是在代码中的其他地方定义的,就像一个函子所说的那样,对于一个普通的函子来说,lambda只不过是语法上的糖而已——它们的性能应该是一样的。像往常一样,试试看:)函数将与最新版本的GCC一起工作,当函数地址是编译时常量时,它会优化间接调用,例如,您调用func(&doNothing)
而不是func(some_func_ptr)
其中some_func_ptr
是一个在其他地方设置了值的变量。我刚刚使用g++进行了检查,它优化了所有内容,lambda、对象以及(使用-O3)函数。如果我只传递一个带有空运算符()的函子,会怎么样?编译器不能消除调用吗(因为操作符()无论如何都不做任何事情)?还是空的lambda?那会更糟simple@fr33domlover你的意思是func([](){})代码>?模板无法编译,因为它试图使用三个参数调用report_func,但不接受任何参数。在我看来,没有参数的func()重载更干净。如果你的意思是func([](int,int,int){})代码>然后仍然func()
更具可读性。我指的是运算符()或lambda,参数正确,但函数体为空。我不同意,重载意味着代码重复,我已经有了,我想避免这种情况duplication@Zadirion,因此您传递它func([](const-TypeA&,const-TypeB&,const-TypeC&){})
insteadSo-definestruct Noop{template void operator()(T&&…)const{}有限公司