web-dev-qa-db-ja.com

テンプレートを使用してラムダをstd :: functionに変換する方法

基本的に、私ができることは、任意の数の任意のタイプのパラメーターを持つラムダを取り、それをstd :: functionに変換することです。私は以下を試しましたが、どちらの方法も機能しません。

std::function([](){});//Complains that std::function is missing template parameters
template <typename T> void foo(function<T> f){}
foo([](){});//Complains that it cannot find a matching candidate

ただし、次のコードは機能しますが、一般的なコードでは機能しないテンプレートパラメータを明示的に指定する必要があるため、私は望んでいません。

std::function<void()>([](){});

私は一晩中関数とテンプレートをいじくり回してきましたが、これを理解することができないので、どんな助けでも大歓迎です。

コメントで述べたように、私がこれをやろうとしている理由は、可変個引数テンプレートを使用してC++でカレーを実装しようとしているからです。残念ながら、ラムダを使用するとこれは恐ろしく失敗します。たとえば、関数ポインターを使用して標準関数を渡すことができます。

template <typename R, typename...A>
void foo(R (*f)(A...)) {}
void bar() {}
int main() {
    foo(bar);
}

ただし、このような可変機能関数にラムダを渡す方法はわかりません。ジェネリックラムダをstd :: functionに変換することに興味があるのは、次のことができるからです。しかし、最終的には、テンプレートパラメータをstd :: functionに明示的に指定する必要があります。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
    foo(std::function<void()>([](){}));
}
61
retep998

テンプレート引数Tを明示的に指定しないと、ラムダ関数オブジェクトをstd::function<T>型の引数として渡すことはできません。テンプレートタイプの推論は、ラムダ関数のタイプを、この場合は実行できないstd::function<T>に一致させようとします-これらのタイプは同じではありません。テンプレートタイプの推論では、タイプ間の変換は考慮されません。

他の方法で型を推測できれば可能です。これは、関数の引数をidentity型でラップして、ラムダをstd::functionに一致させようとして失敗しないようにすることで可能です(依存型は型の推測によって無視されるため)他のいくつかの引数。

template <typename T>
struct identity
{
  typedef T type;
};

template <typename... T>
void func(typename identity<std::function<void(T...)>>::type f, T... values) {
  f(values...);
}

int main() {
  func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8);
  return 0;
}

ただし、後まで値を渡したくないので、これは明らかにあなたの状況では役に立ちません。

テンプレートパラメータを指定したり、テンプレートパラメータを推定できる他の引数を渡したりしたくないため、コンパイラはstd::function引数の型を推定できません。

43

専用/後ろ向きキャストを使用できます。このようなツールができたら

_#include <functional>

using namespace std;

template<typename T>
struct memfun_type
{
    using type = void;
};

template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
    using type = std::function<Ret(Args...)>;
};

template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
    return func;
}
_

FFL()をすべてのラムダ型にと言って、それらを_std::function_の正しいバージョンに変換することができます

_template <typename... Args> void Callback(std::function<void(Args...)> f){
    // store f and call later
}

int main()
{
    Callback(FFL([](int a, float b){
        // do something
    }));

    return 0;
}
_

表示

22

ラムダの呼び出しシグネチャまたは「make_function」の任意の呼び出し可能オブジェクトの推定 に示すように、ラムダ(または単一の呼び出しシグネチャを持つ他のファンクター)の呼び出しシグネチャをその(単一)から推測できます。 operator()

_template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

template<typename T>
struct get_signature_impl { using type = typename remove_class<
    decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;
_

ただし、これはかなり柔軟性のないアプローチです。 R. Martinho Fernandesが言うように、複数のoperator() sを持つファンクターやtemplatedoperator()または(C++ 14)多相ラムダの場合。 bindが最終的な呼び出し試行まで結果タイプの推論を延期するのはこのためです。

13
ecatmur

Derivation、decltype、variadicテンプレート、およびいくつかの型特性を使用して、ラムダに必要なstd :: function型を取得することができます。

_namespace ambient {

    template <typename Function>
    struct function_traits : public function_traits<decltype(&Function::operator())> {};

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const> {
        typedef ReturnType (*pointer)(Args...);
        typedef const std::function<ReturnType(Args...)> function;
    };

    template <typename Function>
    typename function_traits<Function>::function to_function (Function& lambda) {
        return static_cast<typename function_traits<Function>::function>(lambda);
    }

    template <class L>
    struct overload_lambda : L {
        overload_lambda(L l) : L(l) {}
        template <typename... T>
        void operator()(T&& ... values){
            // here you can access the target std::function with
            to_function(*(L*)this)(std::forward<T>(values)...);
        }
    };

    template <class L>
    overload_lambda<L> lambda(L l){
        return overload_lambda<L>(l);
    }

}
_

私はこのように私のコードでそれを使用します:

ambient::lambda([&](const vector<int>& val){ // some code here // })(a);

PS:私の実際のケースでは、このstd :: functionオブジェクトとその引数を汎用カーネルオブジェクト内に保存し、後で仮想関数を介してオンデマンドで実行できます。

7
Alex Kosenkov

カリー化されていませんalreadystd::bind

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );
3
xtofl

C++ 17には、コンストラクター型の推論があります。そのため、std :: functionテンプレート引数の入力を節約できます。これはまったく何もありませんが、少し少ないです。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
   foo(std::function([](){}));
}    
1
user2281723