1. ホーム
  2. c++

[解決済み] std::function vs テンプレート

2022-04-22 14:37:03

質問

C++11のおかげで、私たちは std::function ファンクタラッパーのファミリーです。残念なことに、これらの新しい追加機能については、悪い話ばかりが聞こえてくる。一番多いのは、恐ろしく遅いというものだ。私もテストしましたが、テンプレートと比較すると本当にひどいものでした。

#include <iostream>
#include <functional>
#include <string>
#include <chrono>

template <typename F>
float calc1(F f) { return -1.0f * f(3.3f) + 666.0f; }

float calc2(std::function<float(float)> f) { return -1.0f * f(3.3f) + 666.0f; }

int main() {
    using namespace std::chrono;

    const auto tp1 = system_clock::now();
    for (int i = 0; i < 1e8; ++i) {
        calc1([](float arg){ return arg * 0.5f; });
    }
    const auto tp2 = high_resolution_clock::now();

    const auto d = duration_cast<milliseconds>(tp2 - tp1);  
    std::cout << d.count() << std::endl;
    return 0;
}

111msに対して1241ms。これは、テンプレートはきれいにインライン化できるのに対して function は仮想呼び出しで内部をカバーする。

もちろん、テンプレートには問題があると思います。

  • はヘッダーとして提供されなければなりません。これは、ライブラリをクローズドコードとしてリリースする際にやりたくないことかもしれません。
  • を使用しない限り、コンパイル時間が長くなる可能性があります。 extern template -のようなポリシーが導入されています。
  • テンプレートの要件(概念、誰か?)を表現するきれいな方法は(少なくとも私には)ありません。

したがって、次のように仮定してよいでしょうか。 function として使用することができます。 デファクト ファンクタを渡す標準的な方法と、高いパフォーマンスを期待する場所にはテンプレートを使うべき?


編集する。

コンパイラはVisual Studio 2012です。 なし CTPです。

解決方法は?

一般的に デザイン 選択できる状況 テンプレートを使用する . という言葉を強調しました。 デザイン のユースケースを区別して考える必要があると思うからです。 std::function とテンプレートはかなり違います。

一般に、テンプレートの選択は、より広い原則の一例に過ぎません。 コンパイル時にできるだけ多くの制約を指定する。 . その理由は簡単で、プログラムが生成される前にエラーや型の不一致を検出できれば、バグだらけのプログラムを顧客に出荷することはなくなるからです。

さらに、ご指摘の通り、テンプレート関数の呼び出しは静的に(つまりコンパイル時に)解決されますので、コンパイラはコードを最適化し、場合によってはインライン化するために必要なすべての情報を持っています(呼び出しがvtableを介して行われた場合は不可能です)。

確かにテンプレートのサポートは完璧ではありませんし、C++11 ではまだ概念のサポートが不足しています。 std::function は、その点であなたを救うでしょう。 std::function はテンプレートの代替ではなく、テンプレートが使用できないデザイン状況のためのツールです。

そのようなユースケースの1つは、次のような呼び出しを解決する必要がある場合です。 実行時 特定のシグネチャに従った callable オブジェクトを呼び出すことで、コンパイル時にその具象型が不明であることを示します。 これは、典型的には、潜在的に 異なる型 が必要です。 一律に呼び出す 登録されたコールバックの種類と数は、プログラムの状態とアプリケーションロジックに基づいてランタイムに決定されます。登録されたコールバックの種類と数は、プログラムの状態とアプリケーションロジックに基づいて実行時に決定されます。これらのコールバックは、ファンクタであったり、普通の関数であったり、特定の引数に他の関数をバインドした結果であったりします。

std::functionstd::bind を有効にするための自然なイディオムも提供します。 関数型プログラミング C++では、関数はオブジェクトとして扱われ、他の関数を生成するために自然にキュレーションされ、結合されます。このような組み合わせはテンプレートでも可能ですが、通常、実行時に結合された呼び出し可能なオブジェクトの型を決定する必要があるユースケースで、同様の設計状況が発生します。

最後に、他の状況として std::function は避けられないものです。 再帰的ラムダ しかし、これらの制限は、概念的な区別というよりも、技術的な制限によって決定されるものだと私は考えています。

まとめますと。 デザイン重視 そして、この2つの構成要素の概念的な使用例が何であるかを理解するように努めてください。もし、あなたが行ったような方法でこの2つを比較するのであれば、あなたはこの2つを、おそらく属していない領域に押し込んでいることになります。