2016-01-15 7 views
5

Rozważmy następujący przykład:Wieloznaczność między funkcją const najlepiej pasującej i innych funkcji

#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

To daje błąd:

error: call to member function 'f' is ambiguous 

Kiedy usuwanie const -qualifier, a następnie błąd przestaje istnieć. Podobnie dzieje się tak samo, jeśli dodaję kwalifikator const do drugiego przeciążenia f. To znaczy. jeśli oba przeciążenia są równe const - zakwalifikowane, to wszystko jest w porządku.

Dlaczego tak jest?

Mój kompilator to clang 3.8.

+2

'std :: declval () .f (O (x))' vs 'std :: declval () .f (x)' (dla 'std :: declval () .f (x) '). konwersja zdefiniowana przez użytkownika w porównaniu do promocji stałej. – Jarod42

+0

@ Jarod42 oznacza to, że podczas rozwiązywania moc obydwu alternatyw jest taka sama? – Orient

+0

@Orient To jest definicja niejednoznaczności. – erip

Odpowiedz

2

Funkcje członkowskie mają domyślny parametr this.

więc nazwać jedną z funkcji f kompilator potrzebuje albo przekonwertować this wpisać const S * lub przekonwertować X do O.

Żadna z konwersji dotyczących wszystkich parametrów nie jest lepsza. Tak więc kompilator wydaje błąd.

1

Mark B & Vlad z Moskwy odpowiedzieć dlaczego, po prostu odpowiedzieć w jaki sposób można je połączyć.

należy dodać explicit uniknąć kompilator niejawna konwersja

#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

jeśli typ O jest w 3party lib można dodać szablon, aby to zrobić.

#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' pochodzące z kodu biblioteki. Nie można zmienić jego implementacji. – Orient

2

Why is it so?: Powodem jest tutaj ponieważ const-ności od samego obiektu s jest również uznane w rozdzielczości przeciążenia. Ponieważ s nie jest const, wymagałoby dodania stałej do niejawnego wskaźnika this, aby wywołać const f. Wywołanie niestanowionego f jest dokładnie dopasowane do wskaźnika this, ale wymaga niejawnej konwersji z X do O za pomocą konstruktora konwertującego obiekt O.

Powiązane problemy