ポインタの減衰と推定されたテンプレート: 優先順位のパズルを解く
C の領域では、関数のオーバーロードとテンプレートの推定の間の相互作用は次のようになります。時には予期せぬ結果を招くこともあります。代表的な例は、配列と生のポインターの両方を処理するために関数がオーバーロードされた場合に発生します。次のコードを考えてみましょう。
template <size_t N> void foo(const char (&s)[N]) { std::cout << "array, size=" << N - 1 << std::endl; } void foo(const char *s) { std::cout << "raw, size=" << strlen(s) << std::endl; }
この関数は、最初は配列の長さを出力するために考案されましたが、非配列をサポートするように拡張されました。しかし、この拡張は不可解な曖昧さをもたらします。
foo("hello") // now prints raw, size=5
後者の方がパラメータとより正確に一致しているにもかかわらず、意図した「配列」バージョンではなく「生」オーバーロードが選択されるのはなぜでしょうか?答えは、ポインターの減衰 として知られる微妙な概念にあります。
ポインターの減衰は、配列から対応するポインターへの暗黙的な変換です。この場合、配列「hello」は、その最初の要素への const char * ポインターにサイレントに変換されます。結果として、ポインターを処理するオーバーロードが優先されます。
この動作は、 C での変換のコストに起因します。オーバーロードは、引数をパラメーターに変換するコストを最小限に抑えるために評価されます。この場合、配列からポインターへの変換は、配列から関数へのパラメーター変換よりもコストが低くなります。
この問題を回避するには、2 番目のオーバーロードもテンプレートとして定義できます。
template <typename T> auto foo(T s) -> std::enable_if_t<std::is_convertible<T, char const *>{}> { std::cout << "raw, size=" << std::strlen(s) << std::endl; }
このアプローチでは、ポインター減衰変換が不要になるため、テンプレート バージョンが優先されます。
結論として、ポインター減衰が優先されます。推定されたテンプレートを超えることは、過負荷解決におけるコスト最小化原則の結果です。あいまいさを回避するには、関数をオーバーロードするときに暗黙的な変換とオーバーロードの種類の両方を考慮することが重要です。
以上がC のオーバーロード解決において、ポインタの減衰が推定されたテンプレートよりも優先されるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。