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()のオーバーロードを作成する場合は、report_func()を呼び出す行を削除するだけで、f()の実装コードを複製する必要があります。

私にはこの種の関数がいくつかあります。ReportFuncを使用して呼び出す場合と、使用しない場合があります。したがって、すべてのコードの重複を避けたいと思います。空のラムダやvoidなどを渡す場合、C ++ 11コンパイラでreport_func()を呼び出さないf()のインスタンス化を生成する必要がありますか?そして、report_func()を呼び出す行を削除するのと同じくらい速いですか、それとも空のラムダでさえ、コンパイラーが最適化しないオーバーヘッドがありますか?(私の特定のケースでは、GCCを使用しています)

また、空のラムダが実際にそれを行い、関数f()の戻り型をReportFuncに変更した場合、つまりreport_func引数を返す場合でも、戻り値を変数に格納して呼び出すのは安全ですか?(空のラムダであっても、理論的には可能ですが、実際には何も起こらないことを意味します)

4

3 に答える 3

7

空のファンクターを渡すだけです。

最適化がオンになっている限り、コンパイラーはテンプレートをインスタンス化し、ファンクターへの(空の)呼び出しをインライン化するため、何もしません。最適化して何もしないようにする必要があります。メタプログラミングを気にせずに呼び出しを削除してください。

G ++が同じように「何もしない」ラムダを最適化することを誓うつもりはありませんが、型がわかっていて、その関数呼び出し演算子がインラインで空であることがわかっているので、そうする必要があります。

ラムダの使用には固有のオーバーヘッドはありません。これは、オブジェクトタイプをで宣言し、operator()そのタイプの一時オブジェクトを作成するための単なる構文糖衣です。コンパイラのフロントエンドがそれをすべて行うにはかなり多くの作業が必要ですが、型が存在すると、オプティマイザはそれを同じことを行うユーザー定義の構造体とまったく同じように扱う必要があります。そのため、それを返すのも安全です。これは、ユーザー定義の関数オブジェクトのようなオブジェクト型の単なるインスタンスです。

于 2013-03-07T13:50:41.503 に答える
1

ラムダが参照によってローカル変数をキャプチャしない限り、それを返して後で呼び出すことは安全です (空のラムダの場合、メンバー変数を持たない単なる呼び出し可能なオブジェクトになるため、安全にコピーして返すことができます)。

呼び出しの削除に関しては、ラムダが何もしないことを理解し、呼び出しを削除するのはコンパイラ次第です。

于 2013-03-07T13:32:44.547 に答える
1

このアプローチを試すことができます。これは単純な実装であり、コードの重複がなく、各呼び出しサイトで空のラムダを渡さなければならないという苦痛を軽減します。

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 に答える