2015-09-13 39 views
5

私は任意のタイプと数のパラメータで他の関数を渡してstd::functionにバインドすることが可能なテンプレート関数を作ろうとしています。私はこれを行うために管理:Variadicテンプレート、タイプ控除とstd ::関数

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return bar; 
} 

template <typename Ret, typename... Args> 
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f) 
{ 
    return f; 
} 

int main() 
{ 
    //auto barp = func(foo); // compilation error 
    auto bar = func(std::function<void (int)>(foo)); 

    bar (0); // prints 0 
} 

私はちょうどauto barp = func(foo);を呼び出し、タイプが推測持っていると思いますが、この行は、次のコンパイルエラーを与える:

error: no matching function for call to ‘func(void (&)(int))’ 
    auto barp = func(foo); 
         ^
note: candidate is: 
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>) 
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f) 
          ^
note: template argument deduction/substitution failed: 
note: mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’ 
    auto barp = func(foo); 
        ^

は、なぜそれがでstd::function<_Res(_ArgTypes ...)>に一致するようにしようとしていますint (*)(int)?私は何とかコンパイラを_Res(_ArgTypes ...)からint(int)に展開する必要があると感じていますが、どうですか?

+4

なぜあなたはまったく 'のstd :: function'に変換していますか? 'std :: function'は型消去クラスです:構造体の型情報を取り、大部分を消去します。型控除は引数をとり、その型を導き出し、コードを生成します。あなたは何かを消すためにどんなタイプを推測するように求めています。それは着用者に射撃する銃を作る鎧の服を作るようなものです。タイプイレージャーはほとんどの意味でタイプ控除の*反対*です。非常にまれなことです。それは可能ですが、C++には簡単な理由がありません*実用的なユースケースはありますか? – Yakk

+0

オーバーロードや可変引数リストはどうですか?これはほんのわずかの状況で可能です。あなたはboost_typeを上げるようなものを使うことができます。 – tahsmith

+0

@ Yakk、私は "memoization"関数をコーディングしようとしていました。つまり、同じパラメータで呼び出し可能なものを返し、以前の計算値を探すという点を除いて、元の関数を呼び出す引数の型を返します適切な地図に格納される)。元の関数を格納するには 'std :: function'を使います。これが可能かどうかはわかりません。 – Tarc

答えて

2

関数はstd::functionではないため、1に変換されます。 ただし、関数の引数を推定することで、オーバーロードに関するあいまいさを排除できます。あなたは、元のstd::functionで何をしたいのか

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return 0; 
} 

// Will cause error. 
//int foo(double); 

template <typename Ret, typename... Args> 
std::function<Ret (Args...)> func(Ret f(Args...)) 
{ 
    return f; 
} 

int main() 
{ 
    auto bar = func(foo); 
    bar (0); // prints 0 
} 

より明らかに動作しない、次のようになります。

template<typename T> 
struct A 
{ 
    A(T); 
}; 

template<typename T> 
void func(A<T> a); 

int main() 
{ 
    func(42); 
} 

42Aではない、それはしかし、1に変換することができます。しかし、それを1に変換するには、すでに知られているTが必要です。

+0

'&foo'はちょっと無関係です。呼び出し側の式の外部にある関数名は、C/C++で暗黙的に関数ポインタに変換されます。 – CoffeeandCode

+0

@CoffeeandCode、そう変わった。私はそれがメンバー機能のために必要であるので、一般的にそれを持つことを好みます。しかしここでは単なる複雑さです。 – tahsmith

1

(それがコンパイルされた場合)あなたのコードでは、意味的に等価である。この:別に

int foo(int x){ 
    std::cout << x << std::endl; 
    return x; 
} 

int main(){ 
    auto bar = [](int x){ return foo(x); }; 
    bar(0); 
} 

return x部分から、それはちょうど私があなたの未定義の動作を補正します。

std::functionを返す機能は、多分入力を減らすために、非常に不要です。

std::functionのコンストラクタをラッパー関数なしで簡単に使用できます。

しかし、まだ。

あなたがしたいことをするには、関数ポインタ自体を渡す必要があります。不可能な変換なし。

はこれを試してください:あなたのコードは動作しません

int foo(int x){ 
    return x + 1; 
} 

template<typename Ret, typename ... Args> 
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{ 
    return {fp}; 
} 

int main(){ 
    auto bar = func(foo); 
    std::cout << bar(0) << std::endl; // outputs '1' 
} 

理由は、あなたがfuncに引数を渡す際に場所を取るしようとしている暗黙的な変換です。

私が言ったように、あなたのコードは現在、上でラムダ式を使って示した例と意味的に同じです。関数のラッピングが必要なところでは、ラムダ式を使用することを強くお勧めします。それらははるかに柔軟性があり、ライブラリの機能ではなく言語の中心的な部分です。

は、ラムダは、関数ポインタに変換されていることを覚えておいてください非取り込みます。これ以下が適合している:私たちは関数ポインタの宣言の周り混乱しない

int main(){ 
    auto func = +[](int x){ return x + 1; }; 

    std::cout << "foo(5) = " << func(5) << std::endl; 

    func = [](int x){ return x * 2; }; 

    std::cout << "bar(5) = " << func(5) << std::endl; 
} 

お知らせ:

int main(){ 
    int(*bar)(int) = [](int x){ return x + 1; }; 
    std::cout << bar(0) << std::endl; 
} 

し、あなたの記事にしたいと同様の機能を持っているが、我々はこのような何かを書くことができますまたはライブラリの種類?誰もが読み書きできるようになりました。 One thing to notice in this example is the unary + operator; to perform a conversion to function pointer before assigning it to the variable.実際には非常にの機能のように見えますが、これはあなたがここで達成しようとしているようです。

1

はそのoperator()を通じて数子(ラムダとstd ::関数)をアンラップしてください:

#include <iostream> 
#include <functional> 

int foo(int bar) 
{ 
    std::cout << bar << std::endl; 
    return 0; 
} 

template<typename /*Fn*/> 
struct function_maker; 

template<typename RTy, typename... ATy> 
struct function_maker<RTy(ATy...)> 
{ 
    template<typename T> 
    static std::function<RTy(ATy...)> make_function(T&& fn) 
    { 
     return std::function<RTy(ATy...)>(std::forward<T>(fn)); 
    } 
}; 

template<typename /*Fn*/> 
struct unwrap; 

template<typename CTy, typename RTy, typename... ATy> 
struct unwrap<RTy(CTy::*)(ATy...) const> 
    : function_maker<RTy(ATy...)> { }; 

template<typename CTy, typename RTy, typename... ATy> 
struct unwrap<RTy(CTy::*)(ATy...)> 
    : function_maker<RTy(ATy...)> { }; 

template<typename T> 
auto func(T f) 
    -> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>())) 
{ 
    return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f)); 
} 

int main() 
{ 
    //auto barp = func(foo); // compilation error 
    auto bar = func(std::function<void(int)>(foo)); 

    auto bar2 = func([](int) 
    { 
     // ... 
    }); 

    bar(0); // prints 0 
} 

Demo

関連する問題