2017-03-03 4 views
10

Betrachten Sie diese Klasse:Warum funktioniert dieser Aufruf an Member nicht eindeutig?

class Base{ 
public: 
    void func(double a) = delete; 
    void func(int a) const {} 
}; 

int main(){ 
    Base base; 

    base.func(1); 
    return 0; 
} 

Wenn Klirren kompiliert ++ verwendet, erzeugt es den folgenden Fehler:

clang++ --std=c++11 test.cpp 
test.cpp:22:7: error: call to member function 'func' is ambiguous 
    base.func(1); 

mit g ++, wird eine Warnung erzeugt:

g++ -std=c++11 test.cpp 
test.cpp: In function ‘int main()’: 
test.cpp:22:13: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: base.func(1); 

Warum ist dieser Code mehrdeutig?

+0

Ganzzahlen können einfach in Fließkommatypen konvertiert werden. Während 'func (int)' die beste Übereinstimmung ist, ist 'func (double)' immer noch eine brauchbare Alternative, die den Aufruf mehrdeutig macht. –

+2

Sie scheinen in eine knifflige Logik zu geraten, die const und nicht-const Elementfunktionen beinhaltet. FWIW, das 'func (double)' in eine 'const'-Member-Funktion ändert, behebt das Problem. –

Antwort

10

nicht statische Elementfunktionen, wie die beiden:

void func(double); // #1 
void func(int) const; // #2 

nehmen auch eine implicit object parameter, die in die Überladungsauflösung ([over.match]/p1) wie jedes andere Argument gilt:

Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call. The selection criteria for the best function are the number of arguments, how well the arguments match the parameter-type-list of the candidate function, how well (for non-static member functions) the object matches the implicit object parameter, and certain other properties of the candidate function.

Nachdem die implizite Einbeziehung Object-Parameter in die Member-Funktionen Signaturen, sieht der Compiler zwei Überladungen:

void func(Base&, double); // #1 
void func(const Base&, int); // #2 

und versucht, die beste tragfähige Funktion basierend auf dem Anruf zu wählen:

Base base; 
base.func(1); 

Die Umwandlung base-Base& (die ein nicht-const lvalue vom Typ Base ist) einen Exact Match Rang (direkten Referenzbindung ergibt Identity conversion) - siehe Table 13.Die Umwandlung von base zu const Base& ist auch einen Exact Match Rang jedoch [over.ics.rank]/p3.2.6#1 deklariert eine bessere Conversion-Sequenz zu haben:

— S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers. [ Example:

int f(const int &); 
int f(int &); 
int g(const int &); 
int g(int); 

int i; 
int j = f(i); // calls f(int &) 
int k = g(i); // ambiguous 

nun für den zweiten Parameter, eine Umwandlung von einem integralen prvalue 1-double ist ein Floating-Integral-Konvertierung ([conv.fpint]), die eine Conversion Rang gegeben wird. Andererseits ist 1 bis int eine Identitätskonvertierung, die von einem Exact Match Rang ist. Für dieses Argument wird #2 betrachtet eine bessere Umwandlungsfolge haben ([over.ics.rank]/p3.2.2):

— the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that, [...]

Überlastungs Auflösung erfolgreich zu sein braucht, dass es höchstens einen Parameter existiert, für die Konvertierungssequenzen unterscheiden ([over.match.best]):

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that, [...]

Hier ICS (# 1) ist besser als ICS (# 2), aber wiederum ICS (# 2) ist besser als ICS (# 1), so kann der Compiler nicht zwischen den beiden Überladungen wählen und Mehrdeutigkeiten erkennen.

+0

Ich wollte nur eine ähnliche Antwort posten. Ein Unterschied: Warum schreibst du 'Base *'? 13.3.1.4 sagt, dass der implizite Objektparameter "lvalue reference to cv X" ist, also "Base &". –

+0

Ausgezeichnete Antwort. Nur ein kleines bisschen pingelig: 'const base & 'bindet auch direkt an das Argument vom Typ' base ', also ist die entsprechende implizite Konvertierungssequenz auch eine Identitätskonvertierung; Es gibt keine Qualifizierungskonvertierung. Es ist nur so, dass die Bedingungen in der von Ihnen zitierten Aufzählung erfüllt sind, was die andere Konvertierung (zu "Basis &") besser macht. – bogdan

+0

@bogdan Ich war unsicher über diesen Teil, also vielen Dank. –

0

Wenn eine Funktion überlastet ist, wird zuerst die Überladungsauflösung ausgeführt. Das Programm ist fehlerhaft, wenn die gelöschte Funktion die beste Übereinstimmung ist und ausgewählt ist.

daher Ihr Programm würde die gleichen Fehler wie die folgenden erzeugen, weil es eine implizite Konvertierung von int zu verdoppeln und der Compiler nicht weiß, welche Funktion Sie zu nennen beabsichtigen:

class Base{ 
public: 
    void func(double a) {} 
    void func(int a) const {} 
}; 
+3

Ihre Erklärung fehlt: ja es gibt eine implizite Umwandlung 'int'->' double', aber 'int' ist eine perfekte Übereinstimmung für das Argument. Der letzte Schlüssel liegt bei der 'const' – bolov

-1

es wegen der const Modifizierer in func(int). Die base Instanz ist nicht const. C++ - Compiler scheinen die Methode non-const zuerst zu finden, wenn die Instanz nicht const ist. Und dann haben Compiler gefunden, dass diese Methode gelöscht wurde. Der Compiler gibt also eine Warnung aus.

Versuchen Sie, den Modifikator const zu entfernen, oder verschieben Sie den Modifikator const zu func(double), um die Warnung loszuwerden.

Es scheint, dass diese Warnung nicht über implizite Konvertierung ist. Selbst Sie rufen func von func((int)(1)) ist auch nicht gut.

+1

Sie liegen falsch. Es findet nicht zuerst die erste Methode. Die zwei sind mehrdeutig. – bolov

Verwandte Themen