次のコードは、IタイプT
特定のクラステンプレートのインスタンスであるかどうかを決定するために使用されているC++テンプレートメタプログラミングパターンのコアを実証しますファンクションテンプレートisS
の2つのオーバーロードがあり、期待通りに1
を出力します。私は、すなわち二isS
、からポインタを削除すると、プログラムが予期せず0
出力関数テンプレートポインタ引数でオーバーロード解決
template<class T>
constexpr bool isS(const T) {return false;}
と交換してください。 isS
の両方のバージョンでコンパイルのオーバーロード解決フェーズに進むと、出力はコンパイラーが2番目のオーバーロードを選択していることを意味します。私はこれをGCC、Clang、およびvC++の下でオンラインコンパイラhereを使ってテストしました。これらはすべて同じ結果を生成します。なぜこれが起こるのですか?
私はHerb Sutterの記事"Why Not Specialize Function Templates"を何度も読んでおり、isS
の機能は両方とも基本テンプレートと見なされているようです。これがそうであれば、それはどちらが最も専門的であるかの問題です。直観とthis answerによると、最初のisS
はT
がS<A,B>*
のすべてのインスタンスと一致する可能性があり、S<A,B>*
と一致できないT
の可能性のあるインスタンス化が多数あるため、最も専門的なものであることが期待されます。私は、この動作を定義している作業草案で段落を見つけたいと思いますが、コンパイルのどのフェーズが問題を引き起こしているかは完全にはわかりません。 "14.8.2.4部分的な順序付け中にテンプレート引数を控除する"と関係があるか?
この問題は最初isS
がconst S<A,B>
への参照を取得し、第二のconst T
をとる次のコードは、期待値1
出力することを考えると、特に驚くべきことである:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>&) {return true;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(s)<<std::endl;
return 0;
}
をそれで問題がように見えますポインタがどのように扱われるかと関係があります。
'const T'パラメータは' T'パラメータと同じです(正確な一致を得るため)。 'S *'から 'S const *'への修飾変換が必要です。代わりに、 'main'に' S const s; 'を使ってみてください。 - 他のテストで過負荷を判断できなかった場合、*より特殊な*テストが過負荷解決の非常に遅れて発生します。ここでは、より良いランク(正確な一致と資格の調整)があるため、2番目のものを先に選択することができます。 –
dyp
@dypしかし、コンパイラはなぜ、参照を含むコードスニペットの一番下の 'isS'を選ぶのですか?この場合、コンパイラは 'S&'から 'const S &'への修飾変換を行う必要はありませんか? –
Ose
@Oseいいえ、 'T *'と 'const T *'は修飾修飾の変換です。 'T'は' const T& 'への参照変換であり、引数に直接バインドします。 – TartanLlama