C ++ 11优化作为回调传递的空函数

时间:2013-03-07 13:25:27

标签: 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的情况下调用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?所以在理论上称它是可能的,它只是意味着什么都没有发生)

3 个答案:

答案 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;
}