2017-02-19 29 views
2

任意の数の引数を受け入れるテンプレート関数foo()があるとします。最後の引数が常にstd::functionである場合、CbArgsにこのstd::functionのパラメータが含まれるように、以下に示すfoo()テンプレートを実装するにはどうすればよいですか?テンプレート引数とstd ::関数パラメータの控除

std::function<void(int,int)> cb; 
foo(5, "hello", cb); 

私の最初のアイデアだった:

template<typename... InArgs, typename... CbArgs = ???> 
//          ^^^^^^^^^^^^ 
void foo(InArgs... args) { ... } 

たとえば、次のように呼び出された場合CbArgs{int,int}をする必要があります

template<typename... InArgs, typename... CbArgs> 
void foo(InArgs... args, std::function<void(CbArgs...)>) { ... } 

しかし、これはコンパイルされません:

note: template argument deduction/substitution failed: 
note: mismatched types ‘std::function<void(CbArgs ...)>’ and ‘int’ 
    foo(5, "hello", cb); 

質問1:
なぜこれはコンパイルされませんか?なぜテンプレート引数の控除が失敗するのですか?


は最終的に、私はこのソリューションを思い付いた:ここ

template<typename... InArgs, typename... CbArgs> 
void fooImpl(std::function<void(CbArgs...)>, InArgs... args) { ... } 

template<typename... InArgs, 
     typename CbType = typename std::tuple_element_t<sizeof...(InArgs)-1, std::tuple<InArgs...>>> 
void foo(InArgs... args) 
{ 
    fooImpl(CbType{}, args...); 
} 

CbTypestd::functionあるInArgsの最後のタイプです。その後、CbTypeの一時的なものがfooImpl()に渡されます。ここでCbArgsが推測されます。これは動作しますが、私には醜いようです。

質問2:
は、私は2つの機能とCbTypeの一時的なインスタンスを持つことなく、よりよい解決策があるかどうだろうか?

+0

ですが、機能が最後の位置にある必要がありますか?または、最初の位置にすることができますか? – max66

+0

最後の位置になければなりません。これは、最後の引数としてコールバックを持つ非同期関数をラップするために必要です。 – Kane

+0

"する必要があります" - 私は申し訳ありませんが、私は最後の引数としてコールバックを持たない非同期関数を記述することができます。どのようにして、最後の引数としてコールバックが必要なのでしょうか?あなたはコンテンション・パス・スタイルを好むだけですか?あなたは「あなたがやっていること」の後に「次のことは起こる」と思っていますか?理由はたくさんありますが、「非同期関数は最後の引数としてコールバックを持たなければなりません」という理由をスキップし、結論を述べるだけです。 – Yakk

答えて

3

なぜこれはコンパイルされませんか?なぜテンプレート引数の控除が失敗するのですか?

パラメータパックが最後のパラメータではない、それはを推定することはできません。 InArgs...の内容はあなたのfoo定義の作業を行いますコンパイラに伝える:あなたはあなたの回避策で発見として

template<typename... InArgs, typename... CbArgs> 
void foo(InArgs..., std::function<void(CbArgs...)>) { } 

int main() 
{ 
    std::function<void(int,int)> cb; 
    foo<int, const char*>(5, "hello", cb); 
} 

はまた、単に最後にInArgs...を入れて、あなたのfoo呼び出し更新:

template<typename... InArgs, typename... CbArgs> 
void foo(std::function<void(CbArgs...)>, InArgs...) { } 

int main() 
{ 
    std::function<void(int,int)> cb; 
    foo(cb, 5, "hello"); 
} 

2つの機能を持たず、一時的なインスタンスがCbTypeでない方が良い方法があるのだろうか?ここで

は、不要な一時的なインスタンスを避けることが、CbArgs...の控除のためにあなたと同じメカニズムを使用しての可能性のある方法です:単純に空のラッパーでCbTypeをラップし、代わりにfooImplにそれを渡します。

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

template<typename... InArgs, typename... CbArgs> 
void fooImpl(type_wrapper<std::function<void(CbArgs...)>>, InArgs&&...) { } 

template<typename... InArgs, 
     typename CbType = 
      std::tuple_element_t<sizeof...(InArgs)-1, 
       std::tuple<std::remove_reference_t<InArgs>...>>> 
void foo(InArgs&&... args) 
{ 
    fooImpl(type_wrapper<CbType>{}, std::forward<InArgs>(args)...); 
} 

追加の改善:

  • typename不要だったtypename CbType =後 - それは削除されました。

  • args...は、その値カテゴリを保持するfooImplに完全-転送する必要があります。 foofooImplの両方は、args...とし、転送参照とします。

wandbox example


方法より簡単非終端パラメータパックを扱うになるだろう提案があることに注意してください:P0478R0 - "Template argument deduction for non-terminal function parameter packs"が。それはあなたの元の実装を意図した通りに動作させるでしょう。

+1

そんなに価値がない。 +1。最初に 'std :: function'を置くか、関数呼び出しを複数の呼び出しに分割することをお勧めします。 – Yakk

+0

@Yakk 'std :: function'は最後にする必要があります。 「非同期関数は最後の引数としてコールバックを持たなければならない」(これは決して述べていない)が、この特定のタスクの条件であるためではありません。 – Kane

+0

Vittorio、 'type_wrapper'に関するヒントをありがとう。私はこれのようにそれをやると思う。 – Kane

関連する問題