dlaczego następujący kod się nie kompiluje i kiedy usunę jawne słowo kluczowe przed konstruktorem w klasie A, kompiluje?Wpływ "jawnych" konstruktorów na rozdzielczość przeciążania
Korzystanie z Visual Studio 2013:
enum E { e1_0, e1_1 };
template<typename T>
struct A
{
A() {}
explicit A(unsigned long) {}
A(T) {}
};
struct B
{
B() {}
B(E) {}
};
void F(B) {};
void F(A<short>) {};
void test()
{
F(e1_0);
}
Błąd:
1>------ Build started: Project: exp_construct_test, Configuration: Debug Win32 ------
1> exp_construct_test.cpp
1>e:\exp_construct_test\exp_construct_test.cpp(23): error C2668: 'F' : ambiguous call to overloaded function
1> e:\exp_construct_test\exp_construct_test.cpp(19): could be 'void F(A<short>)'
1> e:\exp_construct_test\exp_construct_test.cpp(18): or 'void F(B)'
1> while trying to match the argument list '(E)'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Edit: Pobrałem szczęk i skompilowany z dzyń-CL, który zgłasza błąd dla obu przypadków. Jak podkreślono w komentarzach, dwuznaczność jest pomiędzy A<short>(short)
i B(E)
.
Więc być może jest błąd w VC++, że po usunięciu explicit
z A(unsigned long)
, kompilator, niezależnie od zamiaru wybiera B (E), aby podnieść błąd dwuznaczności. Czy ktokolwiek może potwierdzić zachowanie klang jako standardową, a VC++ jako buggy?
dodałem
void G(E) {};
void G(short) {};
oraz zaproszenie do G takiego:
G(e1_0);
Które nie zgłosi błąd. Dlaczego tutaj G(E)
jest preferowany, a w przypadku kandydatów A<short>::A(short)
i B::B(E)
są one niejednoznaczne?
End Edit
Dzięki --joja
Jeśli usuniemy słowo 'explicit', to ono również nie zostanie skompilowane, a także nie powinno. Niejednoznaczne wywołanie nie należy do 'A :: A', lecz do' F'. Zauważ, że 'A :: A (T)' nie jest 'jawne', więc wywołanie' F' jest niejednoznaczne. Jeśli * dodasz * an 'explicit' do' A :: A (T) ', to będzie ono rządzić tą konwersją, a kod się skompiluje. – 5gon12eder
Jednoznaczne słowo kluczowe przed konstruktorem zapobiega nieinicjowanemu tworzeniu instancji tego typu za pomocą parametru, który działałby jako argument konstruktora. Kiedy usuniesz "explicite" z konstruktora A, możesz skonstruować A po prostu przekazując 'void F (A)' krótko, czyli co F (e1_0) wydaje się robić –
Prismatic
@ 5gon12eder, którego kompilatora używasz? W VS2013 kompiluje się bez "jawnego". Wiem, że niejednoznaczne wywołanie to 'F', ale przyczyną jest konwersja' e1_0' do typu, który akceptuje przeciążenie 'F'. Kiedy 'explicit' jest usuwany, kompilator wybiera' B (E) ', aby wykonać konwersję i wywołuje' F (B) '. Dodałem "explicite", aby upewnić się, że nie używam domyślnie konstruktora, który jest konstruktorem tylko dla wyjątków w moim prawdziwym kodzie. Moim zdaniem użycie 'explicit' powinno jeszcze bardziej poprowadzić kompilator do użycia konwersji B (E) i nie brać pod uwagę promocji' enum E' na 'unsigned long'. – joja