4

我有一个函数模板:

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 编译器生成一个不调用任何 report_func() 的 f() 实例?它是否与简单地删除调用 report_func() 的行一样快,甚至一个空的 lambda 也会有一些编译器未优化的开销?(在我的具体情况下,我使用 GCC)

另外:如果一个空的 lambda 确实这样做了,并且我将函数 f() 的返回类型更改为 ReportFunc,即它返回 report_func 参数,那么将返回的值存储在变量中并调用它是否仍然安全?(即使它是一个空的 lambda?所以理论上可以调用它,它只是意味着什么都没有发生)

4

3 回答 3

7

只需传递一个空函子。

只要您打开了优化,编译器就会实例化模板,内联对仿函数的(空)调用,因此什么也不做。它应该优化到什么都没有,不要费心元编程来尝试删除调用。

我不会发誓 G++ 会以同样的方式优化“什么都不做”的 lambda,但它应该这样做,因为类型是已知的,并且它的函数调用运算符是内联的并且已知是空的。

使用 lambda 没有固有的开销,它只是用 an 声明对象类型operator()并创建该类型的临时对象的语法糖。编译器前端需要做很多工作才能完成所有这些工作,但是一旦类型存在,优化器就应该将其视为与执行相同操作的用户定义结构完全相同。出于这个原因,返回它也是安全的,它只是一个对象类型的实例,就像一个用户定义的函数对象。

于 2013-03-07T13:50:41.503 回答
1

只要您的 lambda 不通过引用捕获任何局部变量,就可以安全地返回它并稍后调用(对于空 lambda,它只是一个没有成员变量的可调用对象,因此可以安全地复制和返回)。

至于调用消除,由您的编译器决定 lambda 什么都不做并删除调用。

于 2013-03-07T13:32:44.547 回答
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;
}
于 2013-03-07T13:36:52.800 回答