2016-01-15 8 views
5

Nehmen wir folgendes Beispiel:Ambiguität zwischen const am besten passende Funktion und andere Funktion

#include <type_traits> 

#if 1 
struct X {}; 

struct O 
{ 
    O(X) { ; } 
}; 
#else 
struct O {}; 

struct X 
{ 
    operator O() { return {}; } 
}; 
#endif 

static_assert(std::is_convertible< X, O >::value); 

struct S 
{ 
    void f(X) const { ; } 
    void f(O) { ; } 
}; 

#include <cstdlib> 

int 
main() 
{ 
    S s; 
    s.f(X{}); 
    return EXIT_SUCCESS; 
} 

Live example

Es gibt einen Fehler:

error: call to member function 'f' is ambiguous 

Wenn ich const -qualifier entfernen, dann der Fehler hört auf zu existieren. Genauso ist es auch, wenn ich der zweiten Überladung von fconst -qualifier hinzufüge. I.e. Wenn beide Überladungen gleichermaßen const -Qualifiziert sind, dann ist alles in Ordnung.

Warum ist es so?

Mein Compiler ist clang 3,8.

+2

'std :: declval () .f (O (x))' 'vs std :: declval () .f (x)' (für 'std :: declval () .f (x) '). benutzerdefinierte Conversions im Vergleich zur Const-Promotion. – Jarod42

+0

@ Jarod42 bedeutet es, dass während der Auflösung die Stärke beider Alternativen gleich ist? – Orient

+0

@Orient Das ist die Definition von Ambiguität. – erip

Antwort

2

Elementfunktionen haben impliziten Parameter this.

So eine der Funktionen zu nennen f der Compiler muss entweder this konvertieren const S * zu schreiben oder X-O zu konvertieren.

Keine Konvertierung in Bezug auf alle Parameter ist besser. Der Compiler gibt also einen Fehler aus.

1

Mark B & Vlad aus Moskau beantworten, warum ich nur antworten, wie Sie sie nennen.

Sie sollten explicit in den Compiler implizite Konvertierung

#include <iostream> 

struct X {}; 

struct O 
{ 
    explicit O(X) { ; } 

    O() = default; 
    O(O const &) = default; 
    O(O &&) = default; 
}; 

struct S 
{ 
    void f(X) const { ; } 
    void f(O) { ; } 
}; 

#include <cstdlib> 

int main() 
{ 
    S s; 
    s.f(X{}); 
    return EXIT_SUCCESS; 
} 

EDIT

, wenn Ihr Typ O ist in 3party lib vermeiden Sie eine Vorlage, dies zu tun hinzufügen können.

#include <iostream> 

using namespace std; 

struct X {}; 

struct O { 
    O(X) { 
     ; 
    } 

    O() = default; 
    O(O const&) = default; 
    O(O&&) = default; 
}; 

struct S { 
    void f(const X) const { 
     cout << "X" << endl; 
    } 
    void f(O) { 
     cout << "O" << endl; 
    } 
}; 

#include <cstdlib> 

template<typename TA, typename TB> 
void myCall(TA a, TB b) { 
    ((const TA &&)a).f(b); 
} 

template<> 
void myCall(S a, O b) { 
    a.f(b); 
} 

template<> 
void myCall(S a, X b) { 
    ((const S)a).f(b); 
} 

int main() { 
    S s; 
    myCall(s, X()); 
    //s.f(X()); 
    return EXIT_SUCCESS; 
} 
+1

'O' kommt aus einem Bibliothekscode. Die Implementierung kann nicht geändert werden. – Orient

2

Why is it so?: Der Grund hier ist, weil die const-ness des s Objekt selbst ist auch in Überladungsauflösung betrachtet. Da s nicht-const ist, würde es erforderlich sein, const zu dem impliziten Zeiger this hinzuzufügen, um die Konstante f aufzurufen. Der Aufruf der nicht-const f ist eine genaue Übereinstimmung für die this Zeiger, erfordert aber eine implizite Konvertierung von X zu O über O Converting Konstruktor.