2016-10-05 16 views
1

私は関数の戻り値の型を取得するためにstd :: result_ofを使用して関数呼び出しラッパーを作成するためにSFINAEを使用しようとしています。問題を再現するために、少量のサンプル:std :: result_of voidの戻り値の型に失敗しました

GCC 4.9.2の結果でこれをコンパイル
void test(int) {} 

template<typename T, typename... Args, typename R = typename std::result_of<T(Args...)>::type, typename std::enable_if<!std::is_void<R>::value, int>::type* = nullptr> 
int test2(R& ret_param, Args... args) 
{ 
    T* test_call = test; 
    ret_param = test_call(args...); 
    return 0; 
} 

template<typename T, typename... Args, typename std::enable_if<std::is_void<typename std::result_of<T(Args...)>::type>::value, int>::type* = nullptr> 
int test2(Args... args) 
{ 
    T* test_call = test; 
    test_call(args...); 
    return 0; 
} 

int main() 
{ 
    test2<decltype(test)>(1); 
} 

に:問題は、何らかの形で「型名のstd :: result_of ::タイプは」と評価されていることである

68:26: error: no matching function for call to 'test2(int)' 
68:26: note: candidates are: 
46:5: note: int test2(R&, Args ...) [with T = void(int); Args = {}; R = int; typename std::enable_if<(! std::is_void<R>::value), int>::type* <anonymous> = 0u] 
46:5: note: no known conversion for argument 1 from 'int' to 'int&' 
56:5: note: template<class T, class ... Args, typename std::enable_if<std::is_void<typename std::result_of<_Functor(_ArgTypes ...)>::type>::value, int>::type* <anonymous> > int test2(Args ...) 
56:5: note: template argument deduction/substitution failed: 
55:142: error: function returning a function 
55:142: note: invalid template non-type parameter 

関数は関数を返す? SFINAEを使用して、テンプレート関数の戻り値の型が無効な場合に、異なる関数を選択するように強制するための正しい方法は何ですか?テスト関数がintを返す逆の場合に言及する価値があります。さらに、enable_ifがstd :: is_voidの解決のために削除された場合、この場合は動作します(ただし、戻り値の型がある場合は両方の関数が有効な解像度になり、戻り値を期待しない場合はコンパイルが失敗します) )。

+0

まず、 'decltype(test)'は関数型です。これを 'T'に束縛しています。これは、有効化ロジックで戻り型として扱われます。 –

+0

私は 'typename'の代わりに' class'を使うでしょう。私はそれらの複数の100文字の行を避けるでしょう。読書できるように物事を自然に分割してみてください。 –

+0

私が理解しているのは、std :: result_of :: typeは、引数Argsで呼び出された場合の関数Tの戻り値の型です。これは、私が有効化ロジックで使用しているものです。 – Grahalt

答えて

1

の引数には、T_ref(Args...)とする必要があります。

次のコンパイルです。私はいくつかの単純化と再フォーマットを適用しました。

ところで、私はこれは良い考えだとは思わない。代わりに、の2種類の機能の異なる名前が必要です。彼らは結局、異なる議論を必要とする。明示的です。暗黙的に悪いです。

#include <type_traits>  // std::(enable_if, result_of) 

void test(int) {} 
int foo() { return 0; } 

template< 
    class T, class R, class... Args, 
    class T_ref = T&, 
    typename std::enable_if< 
     !std::is_void< 
      std::result_of_t<T_ref(Args...)> 
      >::value 
     >::type* = nullptr 
    > 
int test2(R& ret_param, Args... args) 
{ 
    static_assert(std::is_same< 
     R, 
     std::result_of_t<T_ref(Args...)> 
     >::value, "!"); 
    T* test_call = foo; 
    ret_param = test_call(args...); 
    return 0; 
} 

template< 
    class T, class... Args, 
    class T_ref = T&, 
    typename std::enable_if< 
     std::is_void< 
      std::result_of_t<T_ref(Args...)> 
      >::value 
     >::type* = nullptr 
    > 
int test2(Args... args) 
{ 
    T* test_call = test; 
    test_call(args...); 
    return 0; 
} 

int main() 
{ 
    test2<decltype(test)>(1); 

    int result; 
    test2<decltype(foo)>(result); 
} 
+0

一般的に暗黙的であることに同意する明示的に悪いが抽象的に彼らは同じことをしているので、私は彼らに同じ名前を持たせたい。そうでなければ、他のコードを複雑にするだろう。この例をありがとう、私は最初にrefでTを取ることができ、余分なテンプレートパラメータを避けることができますか?これはうまく動作しています。それは実際よりも見た目がさらに美しくなります:) – Grahalt

+1

'result_of'に' T& 'と書いて、余分なテンプレートパラメータを追加することはできません。 –

1

あなたは、パラメータとしてテスト関数を渡すために受け入れた場合、あなたはすべてのその定型を必要としない:

void test(int) {} 
int test(double) { return 0; } 

template<typename R, typename... Args> 
int test2(R(&f)(Args...), R& ret_param, Args... args) 
{ 
    ret_param = f(args...); 
    return 0; 
} 

template<typename... Args> 
int test2(void(&f)(Args...), Args... args) 
{ 
    f(args...); 
    return 0; 
} 

int main() 
{ 
    test2(test, 1); 
    int r; 
    test2(test, r, .0); 
} 

控除やオーバーロードは、あなたのための作業を行います。

+0

答えを教えていただきありがとうございます。残念ながら、私は関数の宣言しか持たないため、関数をパラメータとして渡すことはできません。実際の関数を渡すと、リンカーのエラーが出るだけです。関数の宣言型のみを使用できます。 – Grahalt

+0

@Grahalt [OK]を、あなたの例に定義を置くので、私はそれを知ることができませんでした。ごめんなさい。 – skypjack

関連する問題