質問

次のコードを検討してください:

template<typename T>
char (&f(T[1]))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

T T [1] に置き換えると

になるため、SFINAEを実行して2番目のオーバーロードを選択することを期待しました
 void [1]()

もちろん、これは無効なタイプです。テンプレートのパラメーターを関数のパラメーターに置き換え、14.8.2 [temp.deduct]などの有効な結果の型を確認した後、パラメーターの型(配列-&gt;ポインター)の調整が行われます。

しかし、comeauとGCCは両方とも上記のコンパイルに失敗します。両方とも診断が異なります。

Comeauのコメント:

  

&quot; ComeauTest.c&quot;、2行目:エラー:関数の配列は許可されていません char(&amp; f(T [1]))[1];

GCCによると(バージョン 4.3.3 ):

  

エラー:ISO C ++はゼロサイズの配列 c

を禁止しています

つまり、GCCは置換に失敗しませんが、 f の最初のオーバーロードを選択し、1の sizeof を返します。コモー。

どのコンパイラが適切で、私のコードはまったく有効ですか?回答の適切な標準セクションを参照するか引用してください。ありがとう!


更新:標準自体には、 14.8.2 / 2 のリストにそのような例が含まれています。なぜ最初に見落としたのかわからない:

template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array

この例は参考情報にすぎませんが、これらすべての不可解な段落の意図を示しており、上記のコードが機能して最初のオーバーロードを拒否する必要があることを示しているようです。

役に立ちましたか?

解決

小さなメモ、非常にまれではありますが、 Comeauコンパイラが間違っていると信じています-しかし、これら 機会は非常にまれなので、常にダブルとトリプルの価値があります 前提を確認してください!

g ++の動作には理由があります。わからない パラメータタイプが調整されるときに正確に指定されます。

次のことを考慮してください:

template<typename T>
struct A
{
  void bar (T[10]);
};

template<typename T>
void A<T>::bar (T*)
{
}

「bar」の定義は、「T [10]」のように正当です。 「T *」に減衰します。私がやります コンパイラが テンプレート宣言に対して8.3.5の調整を実行し、 オーバーロードマッチングに関してもパフォーマンスが向上します。

これを例に適用すると、g ++は次のように処理します。

template<typename T>
char (&f( T* ))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

上記では、置換されたパラメーターは、 関数の配列ではなく、関数。

だから、私にとっての質問は-禁止するものがあるかどうかです 関数パラメーター(8.3.5)の調整は2回ですか?

個人的には、調整を許可するのが理にかなっていると思います それ以外の場合は、関数テンプレートのマッチングが複雑になるため、2回 オーバーロード

結論として、g ++が最初のオーバーロードを選択するのに有効だと思います 減衰する配列パラメーターの処理方法に基づいており、コモーは間違っています 関数の配列の控除に失敗しないようにします。

もちろん、これは(Comauが修正された場合)各コンパイラーを意味するようになりました 別のオーバーロードを選択し、それでも標準になります 準拠! :(

編集:

私のポイントを説明するために、次のコードを検討してください:

template <typename T> void foo ( T * );
template <typename T> void foo ( T * const );
template <typename T> void foo ( T [] );
template <typename T> void foo ( T [10] );
template <typename T> void foo ( T [100] );

void bar () 
{
  foo < void() > ( 0 );
}

ここで、fooは数回宣言され、再宣言されています。コンパイラは、14.8.2にリストされている規則をどの宣言、つまりどのパラメーター型に適用する必要がありますか?

私のポイントは、標準が上記について何も言っていないということです。また、これに関する文言は、「未定義」またはまたは「実装定義」振る舞い。

他のヒント

驚くほど-これはVS2008で機能します。私はそれが必ずしも正しい行動であるかどうかの証拠だとは思わない...

Visual Studioが解釈しています

char (&f(T[1]))[1];

Tのサイズ1の配列を取り、サイズ1のcharsの配列への参照を返す関数として

標準を読むことから理解できるように、テンプレート引数の推論のプロセスを説明しようとします。

  1. 明示的なテンプレート引数は、14.8.2 / 2の説明に従ってチェックされます。
  2. 結果の関数シグネチャは、8.3.5に従って調整されます(つまり、配列からポインタへの減衰が実行されます)。
  3. 暗黙的なテンプレート引数は、14.8.2.1に従って推定されます(これは、手順2で部分的に置換された署名で実行されます)。

最初のオーバーロードの推定はステップ1で失敗します。したがって、オーバーロード解決は2番目のオーバーロードを返します。プログラムの形式が不適切だとは思わない。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top