2017-08-02 4 views
1

このtoyの例を実行しようとすると、矛盾するパラメータパックコンパイラエラーが発生します。誰かがなぜint 'がintとして推定されているのかを明らかにしてもらえますか&ここですか?以下の例では、以下の 'test'関数をintリテラルで実行するとうまく動作します。説明のために事前に感謝!メンバ関数を実行するスレッドを作成するvariadicテンプレートメンバ関数のintとintの不一致

class Test { 
    public: 
    Test() {} 

    ~Test() { 
     t.join(); 
    } 

    void print(int num) 
    { 
     std::cout << num << std::endl; 
    } 

    template<class ...Args> 
    void test(void(Test::*b)(Args...) , Args&&... args) 
    { 
     t = std::thread(b, this, std::forward<Args>(args)...); 
    } 

    std::thread t; 
    }; 

int main() 
{ 
    int a = 123; 
    Test test; 
    test.test(&Test::print, a); 
    // test.test(&Test::print, 123); works 
} 

エラー:

prog.cc: In function 'int main()': 
prog.cc:82:40: error: no matching function for call to 'Test::test( 
void (Test::*)(int), int&)' 
    test.test(&Test::print, a); 
          ^ 
prog.cc:82:40: note: candidate is: 
prog.cc:62:10: note: template<class ... Args> void Test::test(void 
(Test::*)(Args ...), Args&& ...) 
    void test(void(Test::*b)(Args...) , Args&&... args) 
    ^
prog.cc:62:10: note: template argument deduction/substitution failed: 
prog.cc:82:40: note: inconsistent parameter pack deduction with 'int' and 
'int&' 
    test.test(&Test::print, a); 
          ^ 
+0

'printThree'とは何ですか? – Sergey

+0

@ Sergey typo-fixed。ごめんなさい。 – Mozbi

+0

あなたは完璧な転送を使用しています。その全体のポイントは、lvalue引数のlvalue参照型を推定する引数の値カテゴリ、rvalue引数のrvalue参照型を保持することです。それは仕事をしているように見える、まあ、完璧。 –

答えて

6

正確に他の引数に一致するように推定された前方参照型を使用しないでください。あなたがint&する左辺値int推論Argsを渡すとき

パーフェクトArgsを転送します。その後int& &&int&に崩壊します。

要するに、推論された前方参照型を、他の引数と完全に一致させるために使用しないでください。

まれな例外がありますが、これは、以前のコンテキストでもう1つのタイプからすでに推測されていたライブラリスタイルのコード内にあります。

この:

template<class ...Args> 
void test(void(Test::*b)(Args...) , Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

が制約されています。

試してみてください。

template<class F, class ...Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 

まさに最初の引数があることは、あなたの問題ではありません。それはthisへのメンバ関数ポインタかもしれません、thisを最初の引数として取ることができるオブジェクトであるかもしれません。

何らかの理由であなたが最初の引数は、メンバ関数ポインタであることを主張したい場合:

template<class R, class...A0s, class ...Args> 
void test(R(Test::*f)(A0s...), Args&&... args) 
{ 
    t = std::thread(f, this, std::forward<Args>(args)...); 
} 

がそれを過剰に制約はありません。あなたが本当にエラーがtestの代わりに、その本体内のinvokationで起こることを確実にしたい場合は、我々は行うことができます。私たちはSFINAE

template<class R, class...A0s, class ...Args> 
auto test(R(Test::*f)(A0s...), Args&&... args) 
-> decltype((void)((std::declval<Test*>()->*f)(std::declval<typename std::decay<Args>::type>()...))> 
{ 
    t = std::thread(f, this, std::forward<Args>(args)...); 
} 

はこれを無効args...の減衰したコピーをthis->*fを呼び出すことができることに基づきます。

これは通常過剰です。私たちは関数ポインタに引数の控除、およびだけパターンマッチをブロック

template<class T> struct tag_t{using type=T;}; 
template<class T> using no_deduction=typename tag_t<T>::type; 

template<class ...Args> 
void test(void(Test::*b)(Args...) , no_deduction<Args>... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

最後に、我々はこれを行うことができます。bが参照を取得したい場合、これは機能しません。

template<class T> 
struct compatible_arg { using type=T; }; 
template<class T> 
struct compatible_arg<T&> { 
    using type=std::reference_wrapper<T>; 
}; 
template<class T> 
using compatible_arg_t = typename compatible_arg<T>::type; 

template<class ...Args> 
void test(void(Test::*b)(Args...) , compatible_arg_t<Args>... args) 
{ 
    t = std::thread(b, this, std::forward<decltype(args)>(args)...); 
} 

マッピングするT&&std::reference_wrapper<T>からTTへとT&T&&へ:私たちはT&std::reference_wrapper<T>に変身するために、いくつかの余分なタイプのプログラミングが必要と思います。あなたはこのような何かを行うと

template<class F, class ...Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 
+0

あなたの最初の文が重複している可能性があります。 –

+0

'std :: result_of'は廃止予定です。代わりに[std :: invoke_result](http://en.cppreference.com/w/cpp/types/result_of)を使用してください。 –

+0

@mario in [tag:C++ 11]? – Yakk

4
template<class ...Args> 
void test(void(Test::*b)(Args...) , Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

が、それは意味:

しかし、本当に、ただで停止

  • PMFから推測Args...は、最初の引数として渡されました。
  • 次に残りの引数の型と値のカテゴリからそれぞれ独立してArgs...を導き出します。
  • 2つの独立した控除の結果が一致しなければなりません。それ以外の場合はエラーです。

これは実際にはではありません。あなたが実際にやりたいことです。関数のパラメータの型と、それに対応する引数の型と値のカテゴリとの間には、完全一致の関係はないことがよくあります。

実際にはPMFの引数型は必要ありません(cv-修飾子とref修飾子のすべての組み合わせをカバーするためにあまりにも多くのオーバーロードを記述する必要はありません)ので、 「いくつかのタイプのTestのメンバへのポインタ:

template<class F, class... Args> 
void test(F Test::* b, Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

それとも単に制約されていないそれを残す:

01:

template<class F, class... Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 

また、新しいパックを導入することができます

+0

応答していただきありがとうございます。新しいパックを導入する3番目の例では、bの関数シグネチャに基づいて最初のパック(Args)が導出されますか? – Mozbi

+0

@Mozbi、[はい](http://coliru.stacked-crooked.com/a/ff636a54268967d4)。 –

関連する問題