1. ホーム
  2. c++

[解決済み] std::functionはどのように実装されていますか?

2022-11-03 09:25:42

質問

私が見つけた資料によると ラムダ式 は、基本的にコンパイラがオーバーロードされた関数呼び出し演算子とメンバとして参照される変数を持つクラスを作成することによって実装されます。このことは、ラムダ式のサイズはさまざまであり、十分な参照変数があれば、そのサイズは 任意に大きい .

或いは std::function には 固定サイズ しかし、同じ種類のラムダを含むあらゆる種類の callable をラップすることができなければなりません。どのように実装するのでしょうか?もし std::function が内部でターゲットへのポインタを使う場合、そのターゲットへのポインタを 使った std::function のインスタンスがコピーされたり移動されたりした場合はどうなるのでしょうか?ヒープを割り当てる必要があるのでしょうか?

どのように解決するのですか?

の実装は std::function の実装は実装によって異なる可能性がありますが、核となる考え方は型消去を使用することです。これを行うには複数の方法がありますが、些細な(最適ではない)解決策は次のようなものだと想像できます(特定のケース向けに簡略化した std::function<int (double)> の場合、単純化されています)。

struct callable_base {
   virtual int operator()(double d) = 0;
   virtual ~callable_base() {}
};
template <typename F>
struct callable : callable_base {
   F functor;
   callable(F functor) : functor(functor) {}
   virtual int operator()(double d) { return functor(d); }
};
class function_int_double {
   std::unique_ptr<callable_base> c;
public:
   template <typename F>
   function(F f) {
      c.reset(new callable<F>(f));
   }
   int operator()(double d) { return c(d); }
// ...
};

この単純な方法では function オブジェクトは、単に unique_ptr を基本型に格納します。で使われるそれぞれの異なるファンクタに対して function で使われるファンクタごとに、ベースから派生した新しい型が作成され、 その型のオブジェクトが動的にインスタンス化されます。また std::function オブジェクトは常に同じサイズであり、ヒープ内の異なるファンクタのために 必要に応じてスペースを確保します。

実際の生活では、性能上の利点をもたらすさまざまな最適化がありますが、答えを複雑にしてしまうでしょう。型は小さなオブジェクトの最適化を使用することができ、動的ディスパッチは、1 つのレベルの間接性を避けるために、ファンクタを引数として取る自由関数ポインタによって置き換えることができます...しかし、考え方は基本的に同じです。


のコピーがどのように機能するかという問題については std::function のコピーがどのように動作するかという問題について、簡単なテストは、状態を共有するのではなく、内部の呼び出し可能なオブジェクトのコピーが行われることを示します。

// g++4.8
int main() {
   int value = 5;
   typedef std::function<void()> fun;
   fun f1 = [=]() mutable { std::cout << value++ << '\n' };
   fun f2 = f1;
   f1();                    // prints 5
   fun f3 = f1;
   f2();                    // prints 5
   f3();                    // prints 6 (copy after first increment)
}

このテストでは f2 は参照ではなく、呼び出し可能な実体のコピーを取得します。もし、呼び出し可能なエンティティが異なる std::function<> オブジェクトで共有されていた場合、プログラムの出力は5, 6, 7となります。