2016-01-15 27 views
5

Consideriamo seguente esempio:ambiguità tra la funzione const migliore corrispondenza e l'altra funzione

#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

dà un errore:

error: call to member function 'f' is ambiguous 

Quando ho rimuovere const -qualifier, poi l'errore cessa di esistere. Ugualmente lo stesso accade, se aggiungo const -qualificatore al secondo sovraccarico di f. Cioè se entrambi i sovraccarichi sono ugualmente const -qualificati, allora tutto è OK.

Perché è così?

Il mio compilatore è clang 3.8.

+2

'std :: declval () .f (O (x))' vs 'std :: declval () .f (x)' (per 'std :: declval () .f (x) '). conversione definita dall'utente rispetto alla promozione const. – Jarod42

+0

@ Jarod42 vuol dire che durante la risoluzione la forza di entrambe le alternative è la stessa? – Orient

+0

@Orient Questa è la definizione di ambiguità. – erip

risposta

2

Le funzioni membro hanno il parametro implicito this.

Quindi, per chiamare una delle funzioni f il compilatore ha bisogno sia per convertire this digitare const S * o per convertire X-O.

Nessuna conversione per tutti i parametri è migliore. Quindi il compilatore genera un errore.

1

Mark B & Vlad da Mosca rispondere perché, ho appena rispondo come si può li chiami.

si dovrebbe aggiungere explicit per evitare compilatore conversione implicita

#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

se il tipo O è in 3party lib è possibile aggiungere un modello per fare questo.

#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' proveniente da un codice libreria. Impossibile modificare la sua implementazione. – Orient

2

Why is it so?: La ragione ecco perché il const-ness dell'oggetto s stesso è anche considerato nella risoluzione di sovraccarico. Poiché s non è const, è necessario aggiungere const al puntatore implicito this per chiamare il const f. La chiamata al numero non costante f corrisponde esattamente al puntatore this ma richiede una conversione implicita da X a O tramite il costruttore di conversioni di O.