Zmienne » Napisy » Potencjalne błędy
Potencjalne błędy
Nie dołączenie odpowiedniego nagłówka
int main()
{
std::string s = "Hello";
}
🔴 Treść błędu:
- GCC
- MSVC
In function 'int main()':
error: 'string' is not a member of 'std'
2 | std::string s = "Hello";
| ^~~~~~
note: 'std::string' is defined in header '<string>'; did you forget to '#include <string>'?
+++ |+#include <string>
1 | int main() {
error C2039: 'string': is not a member of 'std'
predefined C++ types (compiler internal)(339): message : see declaration of 'std'
error C2065: 'string': undeclared identifier
👌 Rozwiązanie problemu
Dodaj linijkę #include <string>
na górze swojego pliku.
Traktowanie liczb jako napisów
#include <string>
int main()
{
std::string s = 123;
}
🔴 Treść błędu:
- GCC
- MSVC
In function 'int main()':
error: conversion from 'int' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string<char>'} requested
4 | std::string s = 123;
| ^~~
error C2440: 'initializing': cannot convert from 'int' to 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
👌 Rozwiązanie problemu
Użyj funkcji std::to_string
, aby przekonwertować liczbę na napis.
Zobacz Konwersja pomiędzy liczbami i tekstem
i Liczby i napisy po więcej informacji.
Traktowanie napisów jako liczb
#include <string>
int main()
{
std::string s = "123";
s *= 15;
}
🔴 Treść błędu:
- GCC
- MSVC
In function 'int main()':
error: no match for 'operator*=' (operand types are 'std::string' {aka 'std::__cxx11::basic_string<char>'} and 'int')
5 | s *= 15;
| ~~^~~~~
error C2676: binary '*=': 'std::string' does not define this operator or a conversion to a type acceptable to the predefined operator
👌 Rozwiązanie problemu
Użyj funkcji std::stoi
lub std::stof
, aby przekonwertować łańcuch znaków na liczbę.
Zobacz Konwersja pomiędzy liczbami i tekstem
i Liczby i napisy po więcej informacji.
Łączenie dwóch literałów stringowych
#include <string>
int main()
{
std::string s = "Hello " + "World";
}
🔴 Treść błędu:
- GCC
- MSVC
In function 'int main()':
error: invalid operands of types 'const char [7]' and 'const char [6]' to binary 'operator+'
5 | std::string s = "Hello " + "World";
| ~~~~~~~~ ^ ~~~~~~~
| | |
| | const char [6]
| const char [7]
error C2110: '+': cannot add two pointers
👌 Rozwiązanie problemu
Zawsze kiedy widzisz w kodzie C++ "TEKST"
, mówimy na to literał stringowy (ang.: string literal).
Jest to śmieszny termin na wartość wpisaną na sztywno (ang.: hardcoded). Ważne jest jednak to, że literał stringowy to nie to samo co std::string
! Mają inne typy i trochę inne właściwości. Typ literału stringowego to const char[N]
, gdzie N
to długość tego tekstu + 1. Błąd z kompilatora GCC wyżej pokazuje to najlepiej.
const char[N]
to tablicą. Tablice omówimy w późniejszym rozdziale, ale ważne jest to, że
nie można do siebie dodać dwóch tablic, ponieważ nie są one liczbami. std::string
w specjalny sposób zapewnia nam taką możliwość (co również omówimy dalej w kursie).
Najprostszym sposobem na naprawienie tego błędu jest połączenie tych literałów własnoręcznie: "Hello World"
.
Ominięcie wywołania std::getline
#include <string>
int main()
{
int age;
std::string full_name;
std::cout << "Enter your age: ";
std::cin >> age;
std::cout << "Enter your full name: ";
std::getline(std::cin, full_name); // Ta linia wydaje się nie wykonywać
std::cout << "\nYour name is " << full_name << " and your age is " << age;
}
Niespodziewane Zachowanie
Szybko zauważysz, że używająć std::getline
, zaraz po użyciu std::cin >> ...
w jakiejkolwiek formie, wywołanie std::getline
wydaje się być ominięte.
W przykładzie wyżej, program zapyta użytkownika o wiek, użytkownik wprowadziłby, powiedzmy 23
, a następnie program zapytałby o imię, lecz użytkownik nie miałby szansy nic wpisać.
Zamiast tego, program od razu wypisałby Your name is and your age is 23
.
👌 Rozwiązanie problemu
Zrozumienie dlaczego tak się dzieje jest troszkę skomplikowane. Na początek przypomnijmy, że std::cin
wyciąga dane z stdin aż do napotkania białych znaków (spacja, nowa linia lub tabulator). Kiedy użytkownik naciśnie Enter
, aby wprowadzić wiek do programu, znak nowej linii \n
zostaje wprowadzony do stdin. std::cin
sczytuje dane, które użytkownik wpisał i zatrzymuje się na nowej linii nie ignorując jej.
std::getline
określa koniec tekstu na podstawie znaków nowej lini w tekście.
Więc kiedy aby funkcja ta próbuje pobrać od nas kolejne dane z *stdin
aż do napotkania \n
,
od razu widzi ona ten znak, który został tam zostawiony przez poprzednie wywołanie std::cin >> age;
.
Interpretuje więc ona to jako koniec danych i ustawia full_name
jako pusty napis ""
.
Rozwiązaniem tego problemu jest użyciem kolejnej metody - ignore
.
Ta metoda mówi std::cin
, aby ominął on pozostawiony z poprzedniej operacji znak nowej linii.
Za każdym razem, kiedy chcesz użyć std::getline
po użyciu std::cin
, powinieneś wywołać std::cin.ignore()
pomiędzy tymi operacjami, jak w tym przykładzie:
int age;
std::string full_name;
std::cin >> age;
std::cin.ignore();
std::getline(std::cin, full_name);