2

これをコンパイルしようとすると驚いたことにラムダ関数のパラメータがstd::stringに解決され、 testに電話するとstd::stringintまたはWidgetに変換する方法がわかりません。std :: functionとgeneric lambdasで関数のオーバーロードが発生しました:std :: stringがintより優先されました

しかし、コンパイラは最初のものは成功するだろうというとき、代わりに最初のものの第二invok機能を選びだした理由私は疑問に思う:

#include <string> 
#include <functional> 

struct Widget {}; 

bool test(int); 
bool test(Widget); 

void invok(std::function<bool(int)>);   // #1 
void invok(std::function<bool(std::string)>); // #2 

int main() 
{ 
    // error: unresolved overloaded function type 
    // invok(test);    

    // still error: no known conversion from std::string to 
    // int or Widget 
    invok([](auto&& x) {  
     return test(std::forward<decltype(x)>(x)); 
    }); 

} 

を例にa C++ proposalからコピーされたこと。

+3

ああ、SFINAE-非友好的な一般的なラムダ。 –

+1

私はこの質問を承認します。 – Barry

答えて

3

コンパイラは#2を選択しませんでした。それは#2を選ぶことができるかどうかを判断しようとしています。

これを行うには、「この汎用ラムダをstd::function<bool(std::string)>に変換できますか」と尋ねます。

std::functionの変換コンストラクタは、 "std::stringの値で呼び出し可能で、結果のタイプがboolに変換可能な場合にのみ"と言います。

コンパイラは、autostd::stringと推定し、関数呼び出し演算子の署名に置き換えようとします...成功!返品のタイプはautoで、「is convertible」の質問に答えるには実際のタイプが必要です。したがって、関数呼び出し演算子テンプレートの本体をインスタンス化して戻り値の型を調べます。

ud。結局本体はstd::stringのために有効ではありません。ハードエラーと爆発が続きます。

3

オーバーロードは曖昧なままですが、2つの可能性を試すときにコードがハードエラーになることを判断する途中で(intまたは文字列を渡します)。

あいまいさを確認するには、ラムダに->boolを追加して、戻り値を決定するために本文をコンパイルする必要はありません。

ラムダの本体が、サブミッションの失敗がエラーにならない領域にありません。代わりにあなたはそこに辛いエラーを得ます。

簡単な修正は、ラムダを明示的にintにすることです。

あなたは、一般的な解決策たい場合:

#define RETURNS(...) \ 
    noexcept(noexcept(__VA_ARGS__)) \ 
    -> decltype(__VA_ARGS__) \ 
    { return __VA_ARGS__; } 

#define OVERLOADS_OF(...) \ 
    [](auto&&...args) \ 
    RETURNS(__VA_ARGS__(decltype(args)(args)...)) 

、その後

invok(OVERLOADS_OF(test)); 

を(少なくとも近い)正しいことを行います。

このマクロは、ラムダの本体から、末尾の戻り値の型に移動します。そして、失敗(文字列をテストに渡すことはできません)は、置換エラーがエラー(SFINAE)を引き起こさないコンテキストで発生します。だからすべてが動作します。

SFINAEはフレンドリーで、規格を読む必要はありません。しかし、経験則では、コンパイラは関数本体のエラーを置換エラーとして扱う必要がなく、未定義クラスの内容にアクセスすることは困難なエラーであるということがうまく機能します。最初は、線を引いてコンパイラーの作家にとってより合理的な場所に思えたからです。代替は狂気またはODRバグの餌であるため、2番目のものです。

実際には、SFINAE標準のルールはもっと難解であり、最後にC++ 14でチェックしたところ、テンプレートクラスの部分的な専門化の間にSFINAEの省略が必要でした。しかし、多分私はそれを誤解しています。いずれにしても、私が使用する経験則は、標準テキストと同様に有用であるようです。どちらも完璧ではありません。

+0

「SFINAEフレンドリーなエリア」についてのメモを使って回答を改善できますか? –

+0

@ Peregring-lk親指のルールが追加されました。 – Yakk

関連する問題