Warto tysiąc słów:
#include<string>
#include<iostream>
class SayWhat {
public:
SayWhat& operator[](const std::string& s) {
std::cout<<"here\n"; // To make sure we fail on function entry
std::cout<<s<<"\n";
return *this;
}
};
int main() {
SayWhat ohNo;
// ohNo[1]; // Does not compile. Logic prevails.
ohNo[0]; // you didn't! this compiles.
return 0;
}
Kompilator nie narzeka, gdy przekaże liczbę 0 operatorowi nawiasu akceptującemu ciąg znaków. Zamiast tego kompiluje się i kończy się niepowodzeniem przed wejściem do metody z:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Na przykład:
> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)
Zgaduję że
Kompilator domyślnie korzysta z std::string(0)
konstruktora, aby wprowadzić metodę, co daje ten sam problem (google powyższy błąd) bez uzasadnionego powodu.
Pytanie
Czy można to naprawić po stronie klasy, więc użytkownik interfejsu API tego nie odczuwa, a błąd jest wykrywany w czasie kompilacji?
Oznacza to dodanie przeciążenia
void operator[](size_t t) {
throw std::runtime_error("don't");
}
nie jest dobrym rozwiązaniem.
c++
string
std
implicit-conversion
kabanus
źródło
źródło
operator[]()
które przyjmujeint
argument i nie definiuj go.Odpowiedzi:
Przyczyna
std::string(0)
jest prawidłowa, ponieważ0
jest stałą zerową wskaźnika. Zatem 0 odpowiada konstruktorowi ciągów, który przyjmuje wskaźnik. Następnie kod działa z warunkiem wstępnym, do którego nie można przekazać wskaźnika zerowegostd::string
.Tylko literał
0
byłby interpretowany jako stała wskaźnika zerowego, jeśliint
byłaby to wartość czasu działania w , nie miałbyś tego problemu (ponieważ wtedy rozdzielczość przeciążeniaint
szukałaby zamiast tego konwersji). Nie jest też dosłownie1
problemem, ponieważ1
nie jest stałą zerową wskaźnika.Ponieważ jest to problem z czasem kompilacji (dosłowne nieprawidłowe wartości), można go złapać w czasie kompilacji. Dodaj przeciążenie tego formularza:
std::nullptr_t
jest rodzajemnullptr
. I będzie on pasował do żadnego wskaźnika stałej zerowej, czy to0
,0ULL
czynullptr
. A ponieważ funkcja jest usunięta, spowoduje błąd czasu kompilacji podczas rozwiązywania przeciążenia.źródło
std::string
konstruktora nie jest dozwolone przez standard C ++. To nieokreślone zachowanie, więc MSVC może robić, co chce (na przykład zgłasza wyjątek).Jedną z opcji jest zadeklarowanie
private
przeciążenia,operator[]()
które akceptuje integralny argument, i nie definiuj go.Ta opcja będzie działać ze wszystkimi standardami C ++ (od 1998 r.), W przeciwieństwie do takich opcji,
void operator[](std::nullptr_t) = delete
które są poprawne od C ++ 11.Dzięki czemu
operator[]()
jestprivate
członkiem spowoduje diagnosable błąd na swoim przykładzieohNo[0]
, chyba że wyrażenie jest używany przez funkcję składową lubfriend
klasy.Jeśli to wyrażenie zostanie użyte z funkcji składowej lub
friend
klasy, kod zostanie skompilowany, ale - ponieważ funkcja nie jest zdefiniowana - generalnie kompilacja zakończy się niepowodzeniem (np. Błąd linkera z powodu niezdefiniowanej funkcji).źródło