2つの標準変換シーケンスを比較する方法は、含まれている変換のランクを使用します

Dec 11 2020
#include <iostream>
void g(int*);  //#1
void g(int (&arr)[2]);  //#2

void f(int*);  //#3
void f(int const*);  //#4
int main(){
  int arr[2] ={0};
  f(arr);    // choose #3
  g(arr);  //ambiguous
}

上記のコードを検討、#3のためseletecedされf(ptr)、ただし、g(arr)得られるambiguous診断。

最適な機能を選択するためのルールは、次のように定義されています。

標準変換シーケンスS1は、次の場合に標準変換シーケンスS2よりも優れた変換シーケンスです。

  • S1はS2の適切なサブシーケンスです([over.ics.scs]で定義された正規形式の変換シーケンスを比較します。ただし、左辺値変換は除きます。ID変換シーケンスは、非ID変換シーケンスのサブシーケンスと見なされます)または、そうでない場合

だからover.ics.scs#3を見てください

これらは、標準の変換シーケンスをランク付けするために使用されます。変換シーケンスのランクは、シーケンス内の各変換のランクと参照バインディングのランクを考慮して決定されます。

上記のルールの私の理解によれば、なぜ#3が最良の過負荷であるのかを理解できますf(ptr)。つまり、次のとおりです。

S1を(arr => int *)として与えます:

Array-to-pointer conversion -> (identity conversion)  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^                   
     int[2] => int*             int* => int* 

S2を(ptr => int const *)として指定している間

Array-to-pointer conversion -> Qualification conversions ->  identity conversion   
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^ 
     int[2] => int*               int* => int const*           int const* => int const* 

ためidentity conversionの適切な部分配列であるQualification conversions、したがってS1はS2よりも優れています。したがって、#3はの過負荷解決によって選択されf(ptr)ます。

同様のプロセスを使用してどちらが最適かを判断するg(arr)と、問題が発生します。

繰り返しますが、S1を(arr => int *)として与えます

Array-to-pointer conversion -> identity conversion  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^ 
      int[2] => int*              int* => int*

S2 as(arr => int(&arr)[2])が与えられている間

参照型のパラメーターが引数式に直接バインドされる場合、引数式がパラメーター型の派生クラスであるタイプを持っていない限り、暗黙の変換シーケンスはID変換です。この場合、暗黙の変換シーケンスは派生クラスです。 to-base変換

identity conversion
^^^^^^^^^^^^^^^^^^^
  bind to reference   

ここで、identity conversionofS2はの適切なサブシーケンスでArray-to-pointer conversionあるS1ためS1、よりも優れているはずg(arr)です。コンパイラが不平を言ったのは、あいまいな呼び出しである理由は何ですか。

標準の変換シーケンスをランク付けする方法について誤解はありますか?2つの標準ICS(含まれている変換のランク)を比較するにはどうすればよいですか?

回答

2 jackX Dec 11 2020 at 16:02

重要なポイントはここにあります:

S1はS2の適切なサブシーケンスです([over.ics.scs]で定義された正規形式の変換シーケンスを比較します。ただし、左辺値変換除きます。ID変換シーケンスは、非ID変換シーケンスのサブシーケンスと見なされます)または、そうでない場合

つまり、関数呼び出しのg(arr)場合、すべての配列からポインタへの変換がランクの決定に使用されるわけではありません。言い換えると、タイプからタイプint[2]int*、ランクを決定するために使用されたID変換のみがあります。したがって、のS1void g(int*);とのS2void g(int (&arr)[2]);は区別できないICSであるため、コンパイラはあいまいなエラーを出します。

対照として、のコンバージョンvoid f(int*);とは、void f(int const*);ランクを比較するために使用されるidentity conversionqualification conversion、それぞれ。

ルールによると:

ID変換シーケンスは、非ID変換シーケンスのサブシーケンスと見なされます

したがって、のQualification conversionランクよりもランクが悪いと見なされますidentity conversion。だから、void f(int*)競争にワインを飲んだ。

cigien Dec 11 2020 at 16:14

あなたは、適用しようとしている-3.2.1 over.ics.rankこれらの過負荷セットに、しかし、このルールはどちらにも適用されませんfg


の呼び出しが与えられたf(arr);場合、のオーバーロード解決を実行するときf、両方のオーバーロードは配列からポインターへの変換で構成される標準の変換シーケンスを必要とし、両方とも同じランク、つまり完全一致を持ちます。この場合に使用されるタイブレーカーはover.match.best#over.ics.rank-3.2.5です:

標準変換シーケンスS1は、次の場合に標準変換シーケンスS2よりも優れた変換シーケンスです。

..。

S1とS2は、資格変換([conv.qual])のみが異なり、それぞれ同様のタイプT1とT2を生成します。ここで、T1は資格変換によってT2に変換できます。

このルールの後に、ルールがどのように機能するかを示す例があります。

過負荷セットのf場合、T1isint *T2isint const *であり、資格変換によってT1に変換できますT2


コールの場合にはg(arr);オーバーロード解決を行う場合には、過負荷のg(int (&)[2])ようにランク付けされた完全一致に必要な標準的な変換シーケンスであるので、無変換必須

ただし、必要な標準の変換シーケンスは配列からポインターへの変換であるため、オーバーロードg(int*)完全一致としてランク付けされます。

fただし、とは異なり、[over.ics.rank]には、の標準変換シーケンスを明確にするルールがなくg、呼び出しは失敗します。