ラムダ式の型を取得する

C++0x から新たに C++ に追加された要素に、ラムダ式というものがあります。
これは、ちょっとした関数を、その場で定義して使えるようにしたもので、

#include <vector>
#include <numeric>
#include <iostream>
 
int main()
{
  std::vector<double> v = { -1, 2, 1.5, -4 };
 
  double prod = std::accumulate( v.begin(), v.end(), 1,
    []( double x, double y ){ return x * y; } );
 
  std::cout << prod << std::endl; // 12
}

のように使います。具体的には Faith and Brave のラムダ式解説などを参照してください。


さて、そんな便利なラムダ式ですが、その使用にはちょっとした制限があり、
ラムダ式に対し、直接 decltype を使って型を取得することは、不可能になっています:

decltype( [&](){ /* 処理 */ } ) f = [&](){ /* 処理 */ }; // 無理

上記のようなコードを書きたい場合には、代わりに auto を使い

auto f = [&](){ /* 処理 */ }; // おk

と書く必要があります。


これは、よく考えれば当然で、上に挙げた最初の例の場合、
decltype 中のラムダ式と、実際に f に格納されるラムダ式は、お互いに独立したラムダ式ですから、
この両者が同じ型を持つかどうかは、よく分かりません。

仮に二つのラムダ式が同じ型を持つとした場合、じゃあどのような条件で二つのラムダ式は同じ型を持つことになるのか、という話になり、それを定義しようとすると、無駄に規格が大きくなり、コンパイラの実装も大きくなり、コンパイル時間も長くなってしまいますし、仮に同じ型を持たないとした場合には、そもそも decltype で型を取得する意味が無くなってしまいます。


そんなこんなで、 C++0xラムダ式に直接 decltype を使うことは出来ません。
といっても、先程の auto の例のように、大体のケースでは全く問題ないのですが*1
それだと困る場合も、時々ですが出てきます。


例えば、 std::unique_ptr のカスタムデリータに、ラムダ式を使いたい場合を考えます。
http://d.hatena.ne.jp/joynote/20110116/1295207127
のような場合ですね。


上記の例では std::function を使っていますが、これはあまり良くない方法です。
というのも、 std::function は渡された関数の型を消してしまうため、最適化が働きにくく、
実行時効率もメモリ効率も、具体的な型を指定した場合に比べ、劣化してしまうからです。


ではどうするか、というと、まず考えられる方法として、
いったん auto で変数に束縛してから decltype する、という方法があります:

#include <memory>
#include <iostream>
 
class Hoge {};
 
int main()
{
  auto del = []( Hoge* p ){ std::cout << "delete\n"; delete p; };
 
  std::unique_ptr<Hoge, decltype(del)> p( new Hoge(), del );
 
  // ここで delete
}

いったん変数に束縛されてしまえば、ラムダ式といえどもただの変数なので、
decltype を使うことにより、型を得ることは簡単にできます。


また、それと同じような方法として、テンプレートによって型推論させるという手もあり、

template<class T, class D>
std::unique_ptr<T, D> make_unique_ptr( T* p, D d ) noexcept {
  return std::unique_ptr<T, D>( p, std::forward<D>(d) );
}

のようなヘルパ関数を定義し、

auto p = make_unique_ptr( new Hoge(),
  []( Hoge* p ){ std::cout << "delete\n"; delete p; }
);

のように書くという方法もあります。*2


どちらを使うかは、これは好みとしか言えないですが、
auto を使った方は、余計な関数を定義しない利点を生かし、滅多に書かないようなコードに、
関数テンプレートを使った場合は、再利用性が高く読みやすいので、使用頻度が高い場合に、
それぞれ使うといいかと思います。


なお本来は、このように関数内部において std::unique_ptr を直接構築するのは、あまり良くないスタイルで、
実際のプログラミングでは、 std::unique_ptr に格納するようなポインタは、生成関数を用いて

#include <memory>
#include <iostream>
 
class Hoge {};

auto deleteHoge = []( Hoge* p ){
  std::cout << "delete\n";
  delete p;
};

inline std::unique_ptr<Hoge, decltype(deleteHoge)> createHoge() {
  return { new Hoge(), deleteHoge };
}

int main()
{
  auto p = createHoge();
 
  // ここで delete
}

のように書いたほうが安全です。*3

*1:そもそも、 auto を使った方が遥かに簡潔なコードになる場合が殆どです。

*2:この手の関数はライブラリ化しておくと便利です。

*3:うっかりミスしてもメモリリークしない、例外安全性が高い等のメリットがある上、デメリットは特にありません。