2017-12-20 3 views
1

Der folgende Code wird von MSVC 2017 abgelehnt (aber von GCC/Clang akzeptiert):Überladungsauflösung: passt const/ref nicht besser als eine benutzerdefinierte Konvertierung?

class BA 
{ 
private: 
    operator int() const; 
}; 

template <typename A, typename B> 
class Builder {}; 

template <typename C> 
struct Concatenable; 

template <int N> struct Concatenable<char[N]> 
{ 
    using type = char[N]; 
}; 

template <int N> struct Concatenable<const char [N]> 
{ 
    using type = const char[N]; 
}; 

template <> struct Concatenable<BA> 
{ 
    using type = char *; 
}; 

template<typename A, typename B> 
Builder<typename Concatenable<A>::type, typename Concatenable<B>::type> 
operator+(const A &, const B &) 
{ 
    return {}; 
} 

int main() 
{ 
    BA ba; 
    char array[] = {'a', 'b'}; 
    ba + array; 
} 

Fehler:

example.cpp 
40 : <source>(40): error C2666: 'operator +': 2 overloads have similar conversions 
31 : <source>(31): note: could be 'Builder<char *,char [2]> operator +<BA,char[2]>(const A &,const B (&))' 
     with 
     [ 
      A=BA, 
      B=char [2] 
     ] 
40 : <source>(40): note: or  'built-in C++ operator+(__int64, char [2])' 
40 : <source>(40): note: while trying to match the argument list '(BA, char [2])' 
40 : <source>(40): note: note: qualification adjustment (const/volatile) may be causing the ambiguity 

Es sieht aus wie MSVC erwägt char[2]-const char (&)[2] Einstellung nicht besser als die BA zu int Umwandlung. Nach meiner Lektüre des Standards sollte dies nicht wahr sein.

[over.ics.rank] sagt, dass Standard-Konvertierungssequenzen immer bessere Konvertierungen als benutzerdefinierte Konvertierungssequenzen sind; Jetzt:

  • die operator+ aus der Schablone instanziiert verwendet zwei Standard-Konvertierungssequenzen
  • die eingebauten in einer (über ptrdiff_t, char *) eine benutzerdefinierte Konvertierungssequenz und eine Standardkonvertierungssequenz

Deshalb nach den Regeln in [over.match.best] ist meine operator+ immer nicht schlechter als die andere, und es ist besser für einige Argumente, so sollte es bevorzugt werden.

beachte, dass die operator int aus BA entfernen oder in ähnlicher Weise um das Array zu const char[] ändern oder den zweiten Parameter von operator+ Ändern eines Nicht-const nehmen B& machen MSVC glücklich.

Was sehe ich nicht?

+0

Alternativ Kennzeichnung 'Betreiber int' als' explicit' sollte auch funktionieren (warum implizite benutzerdefinierte Umwandlung Betreiber selbst erlaubt?). Obwohl dies ein Fehler in VC++ zu sein scheint. Wird eine Referenz auch als Konvertierung akzeptiert? – VTT

+0

Es ist eine "Identitätskonvertierung", zumindest aus meiner Lektüre ... http://eel.is/c++draft/over.ics.ref#1 – peppe

Antwort

1

Implizite Konvertierungssequenzen werden pro Argument verglichen. Sie vergleichen nicht das ICS für ein Argument mit dem für ein anderes. Das ICS für das erste Argument ist irrelevant, wenn Sie versuchen zu entscheiden, welche Überladung das bessere ICS für das zweite Argumnet hat.

Überladung X ist besser als Überladung Y, wenn es bei keinem der Argumente schlechter als Y ist, und ist besser bei mindestens einem Argument (Ignorieren verschiedener Tiebreakers im späten Stadium). Wenn eine Überladung ein besseres ICS für ein Argument hat, aber ein schlechteres ICS für ein anderes Argument, dann ist keines besser als das andere.

Wie im Slack-Chat besprochen, besteht das Problem hier darin, dass MSVC eine ICS-comparison tiebreaker falsch anwendet, die nur beim Vergleich zweier Referenzbindungen anwendbar sein sollte. Das macht das eingebaute ICS auf dem zweiten Argument besser. Dass dies eine tie-broken Standard-Konvertierungssequenz ist, spielt keine Rolle; es ist immer noch besser.

Weitere Beispiele:

void f(int, ...);  // #1 
void f(double, char); // #2 
f(10, 'c'); // ambiguous: #1 is better for the first argument 
      //   #2 is better for the second argument 

struct C { operator int() const; }; 
void g(C, int);  // #1 
void g(int, double); // #2 
g(C(), 1.0); // ambiguous: #1 is better for the first argument 
      //   #2 is better for the second argument 
+0

'Wenn eine Überladung hat eine bessere ICS für ein Argument aber ein schlechteres ICS für ein anderes Argument, dann ist keines besser als das andere. Aber eine Überladung hat bessere (... nicht schlechtere) ICS für alle Argumente. Verstelle ich das Ranking falsch? – peppe

+0

@peppe In MSVCs Welt mit den Referenz-Bindungsregeln ist das eingebaute 'char [2]' ein besseres ICS als dein 'operator +' 'const char (&) [2]'. –

Verwandte Themen