3

この関数テンプレートがある場合、

template<typename T>
void f(T param) {}

次に、次のように呼び出すことができます。

int i=0;
f<int>(i);//T=int : no need to deduce T
f(i); //T=int : deduced T from the function argument!

//likewise
sample s;
f(s); //T=sample : deduced T from the function argument!

ここで、上記の関数テンプレートのこのバリアントについて考えてみましょう。

template<typename TArg, typename TBody>
void g(TArg param) 
{
   TBody v=param.member;
}

さて、次のように記述した場合、コンパイラはテンプレート引数を推測できますか?

sample s;
g(s); //TArg=sample, TBody=int??

sampleと定義されていると仮定します。

struct sample
{
   int member;
};

基本的に2つの質問があります:

  • コンパイラは、2番目の例でテンプレート引数を推測できますか?
  • いいえの場合、なぜですか?何か問題はありますか?標準が「関数本体からのテンプレート引数の推定」について何も述べていない場合、それは引数を推定できないためですか?それとも、言語がさらに複雑になるのを避けるために、そのような控除を考慮していませんでしたか?または何?

そのような控除についてのあなたの見解を教えてください。


編集:

ちなみに、次のコードを記述すれば、GCCは関数の引数を推測できます。

template<typename T>
void h(T p)
{
        cout << "g() " << p << endl;
        return;
}
template<typename T>
void g(T p)
{
        h(p.member); //if here GCC can deduce T for h(), then why not TBody in the previous example?
        return;
}

この例の実演デモ:http ://www.ideone.com/cvXEA

前の例のデモが機能しない:http ://www.ideone.com/UX038

4

4 に答える 4

5

TBodyおそらく、コンパイラはの型を調べても推論しないとすでに結論付けているでしょうsample.member。これにより、テンプレート推定アルゴリズムがさらに複雑になります。

テンプレート マッチング アルゴリズムは、本体ではなく関数シグネチャのみを考慮します。あまり頻繁には使用されませんが、本体を提供せずにテンプレート化された関数を単純に宣言することは完全に合法です。

template <typename T> void f(T param);

これはコンパイラを満足させます。リンカを満足させるには、もちろん関数本体もどこかに定義し、必要なすべてのインスタンス化が提供されていることを確認する必要があります。ただし、必要なインスタンス化がリンク時に利用可能である限り、関数本体は必ずしもテンプレート関数のクライアント コードに表示される必要はありません。本体は、関数を明示的にインスタンス化する必要があります。たとえば、次のようになります。

template <> void f(int param);

ただし、これは質問に部分的にしか当てはまりません。次のようなシナリオを想像できるためです。2 番目のパラメーターは、提供されたデフォルト パラメーターから推測でき、コンパイルされません。

template<typename TArg, typename TBody>
void g(TArg param, TBody body = param.member);  // won't deduce TBody from TArg

テンプレート マッチング アルゴリズムは、実際の型のみを考慮し、クラスまたは構造体の場合の潜在的なネストされたメンバー型は考慮しません。これにより、明らかに複雑すぎると判断された別のレベルの複雑さが追加されます。アルゴリズムはどこで停止する必要がありますか? メンバーのメンバーなども考慮されますか?

また、以下の例に示すように、同じ意図を達成する他の手段があるため、必須ではありません。

あなたが書くことを妨げるものは何もありません:

struct sample
{
   typedef int MemberType;
   MemberType member;
};

template<typename TArg>
void g(TArg param) 
{
   typename TArg::MemberType v = param.member;
}

sample s = { 0 };
g(s);

同じ効果を得るために。


編集後に追加したサンプルについて:h(p.member)構造体のメンバーに依存しているように見えるため、テンプレート マッチング アルゴリズムは失敗するはずですが、2 段階のプロセスにしたため、そうではありません。

  1. を参照すると、コンパイラは型(テンプレート化されているかどうかに関係なく)g(s);の引数を取る関数を探します。sampleあなたの場合、ベストマッチはvoid g(T p). この時点で、コンパイラはg(T p)まだ本体を見ていません! .
  2. ここで、コンパイラはg(T p)に特化した のインスタンスを作成しますT: sample。そのため、それが typeでh(p.member)あることを認識すると、 typeの引数を取る関数を見つけようとします。テンプレート関数が最適であることが判明しました。p.memberinth()inth(T p)

あなたが書いた場合に注意してください(NOT_A_memberに注意してください):

template<typename T>
void g(T p)
{
        h(p.NOT_A_member);
        return;
}

その場合、コンパイラはステージ 1 で有効な一致を引き続き考慮します。その後、 というメンバーが存在しないg()ことが判明すると、エラーが発生します。sampleNOT_A_member

于 2011-01-25T21:37:35.780 に答える
0

提供されたコードでコンパイラが実行できない可能性があることがいくつかあります。最初の例は、2 番目のテンプレート引数の推定TBodyです。まず、型推定は、コンパイラが呼び出しを照合しようとしているときにのみ、関数の引数に適用されます。その時点で、テンプレート化された関数の定義は見られません。

追加のクレジットとして、コンパイラーが関数定義を調べたとしても、コンストラクターでTBody v = parameter.membera を受け入れることができる無限のデータ型が存在する可能性があるため、コード自体は推定できませんparameter.member

次に、2 番目のコード ブロックについて説明します。それを理解するために、テンプレート コンパイルのプロセス全体は、コンパイラがその呼び出しg(x)ポイントで関数呼び出しを確認したときに開始されます。コンパイラは、最適な候補がテンプレート関数であることを認識し、オーバーロードの解決の一部としてtemplate <typename T> void g( T )型が何であるかを判断します。Tコンパイラは、それがテンプレートの呼び出しであると判断すると、その関数に対してコンパイルの最初のパスを実行します。

最初のパスでは、型の実際の置換なしで構文チェックが実行されるため、テンプレート引数Tは依然として任意の型であり、引数pはまだ不明な型です。最初のパスでは、コードが検証されますが、依存する名前はスキップされ、その意味が想定されます。コンパイラがを認識p.memberし、そのpTがテンプレート引数である場合、それはまだ未知の型のメンバーであると想定します (これが、型である場合にここで で修飾する必要がある理由ですtypename)。呼び出しh(p.member);は型引数にも依存し、T型置換が行われるとすべてが意味を持つと仮定して、そのまま残されます。

次に、コンパイラは実際に型を置き換えます。この段階Tではもはやジェネリック型ではありませんが、具象型を表していsampleます。これで、コンパイラは 2 番目のパスで、最初のパスで残されたギャップを埋めようとします。型の内部を見p.membermember、それが であると判断し、その知識でint呼び出しを解決しようとします。h( p.member );この第 2 段階の前に型Tが解決されているため、これは外部呼び出しと同等です。すべての型が既知であり、コンパイラは type の引数を受け取るg(x)関数呼び出しに最適なオーバーロードを解決するだけで済み、プロセス全体が再び開始、テンプレートhint&hが最良の候補として見つかり、...

メタプログラミングにとって、型推定は関数の実際の署名に対してのみ実行され、本体では実行されないことを理解することが非常に重要であり、それが初心者にとって自明ではない理由です。enable_if関数シグネチャで引数または戻り値の型として (ブーストまたは他の場所から) を使用することは偶然ではありませんが、テンプレートが最良の候補として選択され、置換の失敗が型に変わる前にコンパイラが型の置換に失敗する唯一の方法です実際のエラー (SFINAE ではない)

于 2011-01-25T23:25:16.093 に答える
0

TBodysampleメンバーを持つ唯一の型ではない可能性があるため、あいまいな場合がmemberあります。さらに、g他のテンプレート関数を呼び出した場合、コンパイラは に課せられる他の制限を知る方法がありませんTBody

したがって、いくつかのエッジケースでは、理論的には の適切なタイプを推測することが可能ですがTBody、通常はそうではありません。

于 2011-01-25T21:36:56.027 に答える
0

この機能を一貫した方法で実装できるコンパイラはありません。あなたは単に求めすぎているだけです。

于 2011-01-25T21:33:23.193 に答える