libstdc++ ではstd::function
、ポインター、関数ポインター、またはメンバー関数へのポインターを格納するために適切なサイズと位置合わせがされた共用体型を使用します。そのサイズとアラインメントで格納できる関数オブジェクトのヒープ割り当てを回避しますが、それが「位置不変」である場合に限ります
/**
* Trait identifying "location-invariant" types, meaning that the
* address of the object (or any of its members) will not escape.
* Also implies a trivial copy constructor and assignment operator.
*/
std::tr1::function
コードは実装に基づいており、その部分は大幅に変更されていません。std::aligned_storage
より多くのタイプが位置不変として識別されるように、トレイトを特殊化することで、それを使用して簡素化し、改善できると思います。
ターゲット オブジェクトの呼び出しは、仮想関数呼び出しなしで行われます。型の消去はstd::function
、関数テンプレートの特殊化のアドレスである に単一の関数ポインターを格納することによって行われます。すべての操作は、格納されたポインターを介してその関数テンプレートを呼び出し、実行するように求められている操作を識別する列挙型を渡すことによって行われます。これは、vtable がなく、単一の関数ポインターのみをオブジェクトに格納する必要があることを意味します。
この設計は元の作成boost::function
者によって提供されたもので、boost の実装に近いと思います。理由については、Boost.Functionのパフォーマンスドキュメントを参照してください。つまり、GCCは同じ人物による同様の設計であるため、 GCCstd::function
が よりも高速である可能性はほとんどありません。boost::function
注std::function
: アロケータを使用した構築はまだサポートされていません。必要な割り当ては を使用して行われnew
ます。
メンバー関数とオブジェクトへのポインターを保持するa のヒープ割り当てを回避したいという Emile のコメントに応えて、std::function
これを行うためのちょっとしたハックがあります (しかし、あなたは私からそれを聞いていませんでした ;-)
struct A {
int i = 0;
int foo() const { return 0; }
};
struct InvokeA
{
int operator()() const { return a->foo(); }
A* a;
};
namespace std
{
template<> struct __is_location_invariant<InvokeA>
{ static const bool value = true; };
}
int main()
{
A a;
InvokeA inv{ &a };
std::function<int()> f2(inv);
return f2();
}
トリックは、InvokeA
が の小さなオブジェクト バッファーに収まるほど小さいことですfunction
。特性の特殊化では、そこに格納しても安全であると示されているため、function
はそのオブジェクトのコピーをヒープ上ではなく直接保持します。これはa
、それへのポインターが持続する限り持続する必要がありますが、function
のターゲットがである場合はとにかくそうですbind(&A::foo, &a)
。