70

基本的に、私ができるようにしたいのは、任意の数の任意のタイプのパラメーターを持つラムダを取得し、それを 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()>([](){}));
}
4

10 に答える 10

51

std::function<T>テンプレート引数を明示的に指定せずに、型の引数としてラムダ関数オブジェクトを渡すことはできませんTstd::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引数の型を推測できません。

于 2012-11-13T10:46:26.507 に答える
14

ラムダまたは「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を持つファンクターに対しても、テンプレート operator()化されたファンクターまたは(C ++ 14)多形ラムダに対しても機能しません。これがbind、最終的な呼び出しが試行されるまで、結果タイプの推論を延期する理由です。

于 2012-11-13T10:57:17.683 に答える
3

カリー化はすでに実装されていませんstd::bindか?

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );
于 2012-11-13T10:28:07.833 に答える
3

これはあなたにとって興味深いかもしれません: https://gist.github.com/Manu343726/94769034179e2c846acc

それは私が一ヶ月前に書いた実験です。目標は、Haskell の部分呼び出しクロージャーをエミュレートするファンクターのような C++ テンプレートを作成することでした。つまり、引数を使用してパラメーターを使用して関数をm-n呼び出すと、引数のクロージャーが自動的に作成されます。nm

これは、この実験でできることの一例です。

int f( int a, int b, int c, int d)
{
    return a+b+c+d;
}

int main()
{
    auto foo = haskell::make_function( f );

    auto a = foo , 1 , 2 , 3; //a is a closure function object with one parameter

    std::cout << a , 4 << std::endl; //Prints 10
}

haskell::make_functionいくつかの型特性を使用して、ラムダを含むさまざまな型の関数エンティティを処理します。

auto f = haskell::make_function( []( int x, int y , int z ){ return x*y*z; } );

auto a = f(1,2); //a is functor with one parameter (Using the alternative C++-like syntax)
auto b = a(3); // b is 6

ご覧のとおり、コンマ演算子を使用して Haskell 構文を模倣していますが、それを呼び出し演算子に変更して目的の構文を実現することもできます。

コードを使ってやりたいことを完全に自由に行うことができます (ライセンスを確認してください)。

于 2014-06-05T19:42:23.770 に答える
2

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

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
   foo(std::function([](){}));
}    
于 2019-02-05T07:20:53.100 に答える