liczba całkowita -> reguły konwersji wskaźnika

19

Rozważ następujący kod.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSVC 2017 tego nie kompiluje. Oznacza to, że istnieje niejednoznaczne przeciążone wywołanie, ponieważ 1-1jest takie samo, jak 0i dlatego może zostać przekształcone w double*. Inne sztuczki, takie jak 0x0, 0Llub static_cast<int>(0)też nie działają. Nawet zadeklarowanie const int Zero = 0i wywołanie f(Zero)powoduje ten sam błąd. Działa poprawnie, jeśli Zeronie jest const.

Wygląda na to, że ten sam problem dotyczy GCC 5 i niższych, ale nie GCC 6. Jestem ciekawy, czy jest to część standardu C ++, znany błąd MSVC, czy ustawienie w kompilatorze. Pobieżne Google nie przyniosło rezultatów.

użytkownik1334767
źródło

Odpowiedzi:

18

MSVC uważa 1-1za stałą zerową wskaźnika. Było to poprawne w standardzie dla C ++ 03, gdzie wszystkie całkowe wyrażenia stałe o wartości 0były stałymi wskaźnikami zerowymi, ale zostało zmienione tak, że tylko zero literałów całkowitych jest stałymi wskaźnikami zerowymi dla C ++ 11 z CWG problem 903 . Jest to przełomowa zmiana, jak widać w przykładzie i jak również udokumentowano w standardzie, patrz [diff.cpp03.conv] standardu C ++ 14 (wersja robocza N4140).

MSVC stosuje tę zmianę tylko w trybie zgodności. Więc twój kod będzie się kompilował z /permissive-flagą, ale myślę, że zmiana została zaimplementowana tylko w MSVC 2019, zobacz tutaj .

W przypadku GCC GCC 5 domyślnie pracuje w trybie C ++ 98, natomiast GCC 6 i później domyślnie w trybie C ++ 14, dlatego zmiana zachowania wydaje się zależeć od wersji GCC.

Jeśli wywołujesz fz argumentem stałą wskaźnika zerowego, wówczas wywołanie jest dwuznaczne, ponieważ stałą wskaźnika zerowego można przekonwertować na wartość wskaźnika zerowego dowolnego typu wskaźnika, a ta konwersja ma taką samą rangę jak konwersja int(lub dowolnego typu całkowego) do double.

orzech włoski
źródło
-1

Kompilator działa poprawnie, zgodnie z [over.match] i [conv] , a dokładniej [conv.fpint] i [conv.ptr].

Standardową sekwencją konwersji jest [bla bla] zero lub jedna [...] konwersja zmiennoprzecinkowa, konwersja wskaźnika, [...].

i

Wartość typu liczb całkowitych lub nieokreślonego typu wyliczenia można przekształcić w wartość typu zmiennoprzecinkowego. Wynik jest dokładny, jeśli to możliwe [bla bla]

i

Stała wskaźnika zerowego jest literałem całkowitym o wartości zero lub [...]. Stałą wskaźnika zerowego można przekonwertować na typ wskaźnika; wynikiem jest zerowa wartość wskaźnika tego typu [bla bla]

Teraz rozwiązaniem problemu przeciążenia jest wybranie najlepszego dopasowania spośród wszystkich funkcji kandydujących (które, jako zabawna funkcja, nie muszą być dostępne nawet w miejscu połączenia!). Najlepsze dopasowanie to takie, które ma dokładne parametry lub, alternatywnie, najmniej możliwych konwersji. Może wystąpić zerowa lub jedna standardowa konwersja (... dla każdego parametru), a zero jest „lepsze” niż jeden.

(1-1)jest literałem całkowitym o wartości 0.

Można konwertować dosłownym całkowitą zera do każdego z albo doubleczy double*(lub nullptr_t), z dokładnie jednym konwersji. Zakładając, że zadeklarowano więcej niż jedną z tych funkcji (jak w przykładzie), istnieje więcej niż jeden kandydat, a wszyscy kandydaci są równie dobrzy, nie ma najlepszego dopasowania. Jest niejednoznaczny, a kompilator ma rację.

Damon
źródło
1
Co to 1-1jest literał całkowity ? Jest to wyrażenie zawierające dwa liczby całkowite z wartością 1i -operator.
orzech
@walnut: Prawdopodobnie odwołujesz się do niezręcznego sformułowania „sekwencja cyfr binarnych, cyfr ósemkowych, cyfr lub cyfr szesnastkowych” . Jest to bardzo niefortunne sformułowanie dla czegoś raczej „oczywistego”, co sugeruje coś, co nie jest prawdą (tj. Wykluczając znak minus). Przy samych „cyfrach” i pedantycznie zgodnie z definicją „cyfry” (jedna z 0 ... 9), nie można mieć żadnych literałów ujemnych (takich jak -1). Które, ponieważ domyślny typ jest podpisany , jest oczywiście konieczne i jest oczywiście możliwe (i powszechnie akceptowane).
Damon
1
Mam na myśli gramatykę dla liczb całkowitych pokazaną w standardowym łączu, która nie pasuje 1-1. C ++ nie ma ujemnych literałów całkowitych. -1jest wyrażeniem złożonym z 1literału całkowitoliczbowego (typu podpisanego) i -jednoargumentowego operatora minus. Zobacz także sekcję „Notatki” na cppreference.com .
orzech
Z pewnością prawdą jest, że gramatyka tego nie ma, ale jest to całkowicie nieistotne. Z konieczności iz definicji C ++ bardzo ma negatywne literały, ponieważ chyba że wyraźnie je dodasz u, twoje literały są, z definicji, podpisane. Podpisane typy mają wartości ujemne (około 50% możliwych wartości jest ujemnych). To niefortunne, że gramatyka (z tego powodu, którego nie wiedziałbym) wprowadza w błąd w ten sposób i chociaż technicznie (zgodnie z gramatyką) -1 jest literałem dodatnim, negowanym, wszelkimi innymi środkami jest oczywiście negatywny dosłowny. Podobnie jak 3 + 4 jest dosłowne.
Damon
Przy okazji - próbowałem 0U. Ten sam problem. To, czego nie próbowałem, to enumwartość. Może nazwany zmieniłby wszystko. Skończyło się na tym, że napisałem długie wyrażenie z decltypei remove_reference.
user1334767