2012-09-22 17 views
5

私は提供されたラムダを呼び出す関数を提供しようとしていますが、戻り値の型がvoidの場合、デフォルト値を返すことができるかどうか疑問に思っています。C++ 11の戻り値と一致する方法はありますか?

は、ここでラムダの戻り値を返す関数は、私がこれまで持っているものだが、ラムダはvoidであれば、それは

test.cpp:20:68: error: call of overloaded ‘f(main()::<lambda()>)’ is ambiguous 
、これはエラーを生成します20.

#include <functional> 
#include <iostream> 

template <typename H> 
auto f(H&& h) -> decltype(h(), void()) 
{ 
    return h(); 
} 

template <typename H> 
auto f(H&& h) -> decltype(h(), int()) 
{ 
    h(); 
    return 20; 
} 

int main() 
{ 
    int r = f([](){ std::cout << "test1" << std::endl; return 10; }); // error here 
    std::cout << "r: " << r << std::endl; 
    r = f([](){ std::cout << "test2" << std::endl; }); 
    std::cout << "r: " << r << std::endl; 

    return 0; 
} 

返します

これは明らかに、これはC++が戻り型に基づく多型を使用できないためです。しかし、decltypeのような良いC++ 11のトリックがあるのか​​、これを可能にするテンプレートマジックがあるのだろうかと疑問に思っています。コンパイラはvoidの戻り値の型を推測して、intまたはvoidのバージョンがfであるかどうかはあいまいだと言っていますが、これはちょっとばかげているので、私は尋ねています。

f関数が戻り値を期待しているが、ユーザーがreturn文を含まないラムダを提供する場合、コンパイラはvoid型を推測し、エラーを出力します。実際のシナリオでは、ユーザが提供する気にしていないときに合理的なデフォルトの戻り値がどれくらいあるべきかを知っているので、ユーザがしばしば不要なreturnステートメントを無視できるようにコンパイラを設定できるかどうか疑問に思っています便宜上

ありがとうございました。私はGCC 4.6.3を使用していることを言及する必要があります。

回答

template <typename H> 
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), void>::value, int>::type 
{ 
    h(); 
    return 20; 
} 

template <typename H> 
auto f(H&& h) -> typename std::enable_if<std::is_same<decltype(h()), int>::value, int>::type 
{ 
    return h(); 
} 

ありがとう:enable_ifを使用してのXEOの提案に基づいて、私が動作しているようです以下、思い付きました!

+0

として、あなたが通常の機能にFをテストがありますか? – CharlesB

+0

はい、通常の機能ではあいまいです。 – Steve

+0

'decltype'の第2引数:何のために尋ねることはできますか? (本当に、わからないから!)。 – 0x499602D2

答えて

3

std :: enable_ifを使用できます。

返品のタイプは、最初の商品の場合はstd::enable_if<!std::is_same<decltype(h()), void>::value, decltype(h())>:type、2番目の商品の場合はstd::enable_if<std::is_same<decltype(h()), void>::value, int>::typeとなります。それはあなたのコードが動作する必要があります。私はそれをテストしていないので、完全に動作するかどうかはわかりません。

+0

私はまだ "あいまいな"エラーを返すのが怖いです。 – Steve

+0

@スティーブ:いいえ、これは正しく動作します。あなたはどこかでタイプミスがあるかもしれないと思います。 [this](http://liveworkspace.org/code/e0c94a11d120300743e202f4daec8a66)を参照してください。 – Xeo

+0

@ Xeo:ああ、ありがとう、私はJKorのソリューションの最初の部分の2番目の 'decltype(h())'が 'f()'の戻り値を変更するので、問題があると思います。私はあなたの例に基づいて動作している、私は質問にコードを掲載します。 – Steve

2

コンパイラがなぜ関数内で何を行うかに基づいて選択するオーバーロードを推論できる理由を教えてください。 C++では、すべての引数がパラメータと一致する場合にのみ、オーバーロードの解決が関係します。戻り値の型と関数の内容は興味がありません何かとある

As STL says

TLDR:テンプレートは貪欲です!何が起こるかを絶対に確かめない限り、過負荷にしないでください。

パラメータとしての普遍的な参照(template<class T> void f(T&&);)は、あなたが得ることができる最も貪欲なものです。

あなたは@JKorがSFINAEと言うようにそれを解決しますが、簡素化することができます:GCC 4.7はdecltype(int(void_returning_function()))関数が出てSFINAEされることはありませんバグを持っていることを

#include <type_traits> 

template<class F> 
auto f(F f) 
    -> decltype(true? f() : void()) 
{ 
    f(); 
} 

template <class F> 
auto f(F f) -> decltype(int(f())) 
{ 
    f(); 
    return 42; 
} 

int main(){ 
    f([]{}); 
} 

注意を。クラン3.1が正しく

f([]{}); 

作業を行い、aswell

f([]{ return 42; });