2017-01-24 7 views
7
#include <vector> 
#include <functional> 

template<class F> 
class Foo 
{ 
public: 
    template <class T> 
    void std_function(std::function<F(std::vector<T>)> functor) 
    { 
     /* something */ 
    } 

    template <class T> 
    void func_ptr(F (*funtor)(std::vector<T>)) 
    { 
     /* something else */ 
    } 
}; 

template<class T, class F> 
F bar(std::vector<T>) 
{ 
    return F(); 
} 

int main() 
{ 
    Foo<double> test; 
    std::function<double(std::vector<int>)> barz = bar<int, double>; 

    test.std_function(bar<int, double>); //error 1 
    test.std_function(barz); //OK 1 
    test.func_ptr(bar<int, double>); //OK 2 

    test.std_function(bar<int>); //error 2::1 
    test.func_ptr(bar<int>); //error 2::2 

    return 0; 
} 

質問1.のstd ::関数(または関数ポインタ)として(部分的に)テンプレートテンプレート関数を渡す

ラインエラー1:私は明示的にインスタンス化テンプレート関数(bar<int, double>)を渡すしようとしていますstd::functionとなりますが、合法ではありません。

ラインOK 1:私はstd::function<double(std::vector<int>)>bar<int, double>をラップし、ラップファンクタを渡すと、それは今の法律です。

ラインOK 2:私はFoo::func_ptrを通じてbar<int, double>を渡す場合は、代わりにstd::functionの引数として関数ポインタを取得し、それはまた、合法的です。

私はラインを作りたいエラー1が合法です。行OK 2のように、bar<int, double>をラッパーなしで渡すことができます(行OK 1と異なります)。同じフォームを使用してください。しかし、パラメータタイプは異なります。私はstd::functionとして渡したい、関数ポインタではありません。

質問2.

ラインエラー2 :: 1、:: 2 2:私は、私はクラスFooは、そのクラステンプレートとしてbarの戻り値の型を推論したい、ここで達成しようとしていますどのようなF(上記コードの場合は、Fdoubleです)。だから私はちょうどbar<int>として渡すことができ、bar<int, double>ではありません。

しかし、bar<int>からFoo::func_ptrまで渡してもエラーが発生するので、控除に失敗したようです。このコードを私の意図としてどのように機能させることができますか?エラー1の場合

答えて

3

、何が起こっていることは、コンパイラはstd::functionTの代わりにしようとしていることですが、それは最終的には関数ポインタとstd::functionは異なるタイプであることができないため、とstd::function

に関数ポインタに定義された変換はありません

これが働いた:

std::function<double(std::vector<int>)> barz = bar<int, double> 

std::functionを巧みにそれが必要とする型に変換される任意の呼び出し可能に受け入れることができますコンストラクタを持つように型消去で書かれていたので。 std::functionのテンプレート引数を既に指定しているので、これは上記のエラーの型減算と同じではないことに注意してください。

Foo::std_functionを正しく動作させるために少し作業を行うことができます。まず、転送の参照を取るためにその署名を変更します。

template <class T> 
void std_function(T&& functor){/*I'll talk about this in a bit*\} 

その後、我々はそのタイプを決定するために、いくつかのヘルパー構造体を使用して(あなたがそれに合格するために探しているものを、私は知らない)内部的に私たちのstd::functionを構築することができます。関数ポインタのために我々は、以下の操作を行うことができます。

// base 
template<class... T> 
struct function_type_impl; 

// specialization for function ptrs and static class fns 
template<class Ret, class... Args> 
struct function_type_impl<Ret(*)(Args...)> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

// type alias so we don't need to keep typing typename ... ::type 
template<class... T> 
using function_type = typename function_type_impl<std::decay_t<T>...>::type; 

、その後、我々はstd_function署名を変更することができます。

template <class T> 
void std_function(T&& functor) 
{ 
    function_type<T> myFunction = std::forward<T>(functor); 
    // do something with our std::function 
} 

私たちなら、あなたは、しかし

test.std_function(&::bar<int, double>); 

としてそれを呼び出すことができますがより完全なものにしたいと思っていて、ファンクタ、ラムダ、さらにはstd::functionsを受け入れるには、さらに専門分野を追加することができます:

namespace detail 
{ 
template<class... T> 
struct function_type_impl; 

template<class Callable> 
struct function_type_impl<Callable> 
{ 
    using type = typename function_type_impl<decltype(&Callable::operator())>::type; 
}; 

template<class C, class Ret, class... Args> 
struct function_type_impl<Ret(C::*)(Args...) const> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

template<class Ret, class... Args> 
struct function_type_impl<Ret(*)(Args...)> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

template<class... T> 
using function_type = typename function_type_impl<std::decay_t<T>...>::type; 
}// detail namespace 

そして今、次のようにも、動作します:エラー2 :: 1及び2 :: 2については

struct MyFunctor 
{ 
    double operator()(std::vector<int>) const 
    { 
     return 42; 
    } 
}; 

struct MyFunctor2 
{ 
    static double foo(std::vector<int>) 
    { 
     return 42; 
    } 
}; 

int main() 
{ 
    Foo<double> test; 
    std::function<double(std::vector<int>)> barz = bar<int, double>; 
    test.std_function(&::bar<int, double>); 
    test.std_function(barz); 
    test.std_function([](std::vector<int>)->double{return 42;}); 
    test.std_function(MyFunctor{}); 
    test.std_function(MyFunctor2::foo); 
} 

Live Demo

が、問題は単純です。関数が完全にインスタンス化されるまで、関数は全く存在しません。つまり、部分的にテンプレート化された関数への関数ポインタを作成することはできません。関数ポインタを取得しようとするときは、すべてのテンプレート引数を指定する必要があります。第2項の場合

test.func_ptr<int>(bar); 
+2

:[無効counter_example(ダブル(すでに戻り値の型を指定しているので、あなたはTのために何を推測するために明示的にfunc_ptrを伝える場合、コンパイラはあなたのためのポインタの残りの部分をインスタンス化できるようにすることができます*)(std :: vector ));](http://coliru.stacked-crooked.com/a/eb9979b58f3f76e7) - テンプレート引数を渡さずに関数テンプレートを関数ポインタに変換できます。 – Yakk

+0

@ヤク:ああ、私はそれを知らなかった。学習していただきありがとうございます。投稿が更新されました。 – AndyG

+0

だから、関数ポインタを使うのがこの場合の最良の選択ですか? – Gear

関連する問題