web-dev-qa-db-ja.com

ラムダ式の「タイプ」を表現できますか?

ラムダ式を呼び出し可能オブジェクトの「構文糖」と考えて、名前のない基本型を表現できますか?

例:

_struct gt {
    bool operator() (int l, int r) {
        return l > r;
    }
} ;
_

ここで、[](int l, int r) { return l > r; }は上記のコード(および必要なgtの呼び出し可能なオブジェクトの作成)のエレガントな置き換えですが、gt(タイプ)自体を表現する方法はありますか?

簡単な使い方:

_std::set<int, gt> s1;  // A reversed-order std::set
// Is there a way to do the same using a lambda?
std::set<int, some-magic-here-maybe([](int l, int r) { return l > r; }) > s2;
_
52

いいえ、decltypeに入れることはできません。

ラムダ式は、評価されていないオペランドには現れません

次のこともできます

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> s(n);

しかし、それは本当に醜いです。それぞれのラムダ式が新しい一意の型を作成することに注意してください。その後、別の場所で次の操作を行うと、tsとは異なる型になります

auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> t(n);

ここではstd::functionを使用できますが、ラムダ関数オブジェクトの呼び出し演算子への間接呼び出しが必要になるため、実行時にわずかなコストが発生することに注意してください。これはおそらく無視できますが、たとえばstd::sortにこの方法で関数オブジェクトを渡したい場合は重要になることがあります。

std::set<int, function<bool(int, int)>> s([](int l, int r) { return l > r; });

いつものように、最初のコード、次にプロファイル:)

あなたの質問への直接の答え:いいえ。

明確に定義された型を持つファンクターに似た任意の型から割り当て可能なものを使用する必要があります。 1つの例は、sbiの回答に示されているstd :: functionです。しかし、それはラムダ式のタイプではありません。

1
Edward Strange

少なくともMicrosoft Visual Studioでは(他のコンパイラでこれを試したことはありません)、何もキャプチャしない場合、型は通常の関数ポインターのようです。

std::string (*myFunctionPointer)(int x) = [] (int x) {
  char buffer[10];
  return std::string("Test ") + itoa(x, buffer, 10);
};
std::string testOutput = myFunctionPointer(123);

小さなクラスlambda_wrapper <>を使用して、低コストでラムダをラップできます。 std :: functionよりもはるかに高速です。これは、仮想関数呼び出しと動的メモリ割り当てがないためです。ラッパーは、ラムダ引数リストと戻り値の型を推定することで機能します。

#include <iostream>
#include <functional>
#include <set>

template <typename T, typename ... Args>
struct lambda_wrapper : public lambda_wrapper<decltype(&T::operator())(Args...)> {};

template <typename L>
struct lambda_wrapper<L> {
private:
    L lambda;

public:
    lambda_wrapper(const L & obj) : lambda(obj) {}

    template<typename... Args>
    typename std::result_of<L(Args...)>::type operator()(Args... a) {
        return this->lambda.operator()(std::forward<Args>(a)...);
    }

    template<typename... Args> typename
    std::result_of<const L(Args...)>::type operator()(Args... a) const {
        return this->lambda.operator()(std::forward<Args>(a)...);
    }
};
template <typename T>
auto make_lambda_wrapper(T&&t) {
    return lambda_wrapper<T>(std::forward<T>(t));
}
int main(int argc, char ** argv) 
{
    auto func = make_lambda_wrapper([](int y, int x) -> bool { return x>y; });
    std::set<int, decltype(func)> ss(func);
    std::cout << func(2, 4) << std::endl;
}
0
barney