Czytam Accelerated C ++ autorstwa Koeniga. Pisze, że „nowy pomysł polega na tym, że możemy użyć + do połączenia łańcucha i literału ciągu - lub, w tym przypadku, dwóch łańcuchów (ale nie dwóch literałów łańcuchowych).
W porządku, to chyba ma sens. Teraz przejdźmy do dwóch oddzielnych ćwiczeń, które mają to wyjaśnić.
Czy poniższe definicje są prawidłowe?
const string hello = "Hello";
const string message = hello + ",world" + "!";
Teraz próbowałem wykonać powyższe i zadziałało! Więc byłem szczęśliwy.
Potem spróbowałem wykonać następne ćwiczenie;
const string exclam = "!";
const string message = "Hello" + ",world" + exclam;
To nie zadziałało. Teraz rozumiem, że ma to coś wspólnego z faktem, że nie można łączyć dwóch literałów ciągów, ale nie rozumiem semantycznej różnicy między tym, dlaczego udało mi się uruchomić pierwszy przykład (to nie jest „, world” i „! „dwa literały łańcuchowe? Czy to nie powinno działać?), ale nie drugi.
const string message = "Hello" ",world" + exclam
(np. pominięcie pierwszego+
) powinno działać dobrze."Hello" + ", world!"
kiedy możesz"Hello, world!"
. Jak zwykle C ++ ma niesamowite i proste obejście zauważonego problemu. :-)"Hello" ", world!"
(bez+
). Jest wiele skarg, które można by złożyć na temat C ++, ale nie sądzę, aby rozpatrywanie tutaj było jedną z nich. To dokładnie to samo, co gdybyś pisał1 / 3 + 1.5
i narzekał, ponieważ podział był całkowy. Na dobre lub na złe, tak działa większość języków."hello" " world" == "hello world"
jest przydatna, jeśli musisz napisać długi ciąg i nie chcesz, aby wyszedł z twojego okna lub chcesz znaleźć się w ograniczeniu długości linii. Lub jeśli jeden z ciągów jest zdefiniowany w makrze.Odpowiedzi:
+
Operator lewej do prawej asocjatywnego, więc równoważne nawiasach jest wyrażenie:Jak widać, dwa literały łańcuchowe
"Hello"
i",world"
są „dodawane” jako pierwsze, stąd błąd.Jeden z pierwszych dwóch łączonych ciągów musi być
std::string
obiektem:Alternatywnie możesz wymusić, aby druga
+
była oceniana jako pierwsza, umieszczając w nawiasach tę część wyrażenia:Ma sens, że pierwszy przykład (
hello + ",world" + "!"
) działa, ponieważstd::string
(hello
) jest jednym z argumentów po lewej stronie+
. To+
jest oceniane, wynik jeststd::string
obiektem z połączonym ciągiem, a ten wynikstd::string
jest następnie łączony z"!"
.Jeśli chodzi o to, dlaczego nie można łączyć dwóch literałów łańcuchowych przy użyciu
+
, dzieje się tak , ponieważ literał ciągu to po prostu tablica znaków (const char [N]
gdzie gdzieN
jest długością łańcucha plus jeden dla terminatora null). Kiedy używasz tablicy w większości kontekstów, jest ona konwertowana na wskaźnik do jej elementu początkowego.Tak więc, kiedy próbujesz zrobić
"Hello" + ",world"
, to, co naprawdę próbujesz zrobić, to dodać dwaconst char*
s do siebie, co nie jest możliwe (co oznaczałoby dodanie dwóch wskaźników razem?) I gdyby tak było, nie zrobiłoby tego, co chciałem to zrobić.Zauważ, że możesz łączyć literały łańcuchowe, umieszczając je obok siebie; na przykład następujące dwa są równoważne:
Jest to przydatne, jeśli masz długi literał ciągu, który chcesz podzielić na wiele wierszy. Muszą to być jednak literały łańcuchowe: to nie zadziała w przypadku
const char*
wskaźników aniconst char[N]
tablic.źródło
const string message = "Hello" + (",world"+exclam);
będzie działać, ponieważ jawne nawiasy (czy to słowo?).const string message = ((hello + ",world") + "!");
"Hello" ",world"
składnia jest przydatna nie tylko do dzielenia na wiele wierszy, ale także wtedy, gdy jeden z literałów ciągu jest makrem (lub nawet obydwoma). Następnie konkatenacja odbywa się w czasie kompilacji.Zawsze powinieneś zwracać uwagę na typy .
Chociaż wszystkie wyglądają jak łańcuchy
"Hello"
i",world"
są dosłowne .W twoim przykładzie
exclam
jest tostd::string
przedmiot.C ++ ma przeciążenie operatora, które pobiera
std::string
obiekt i dodaje do niego kolejny ciąg. Kiedy łączyszstd::string
obiekt z literałem, spowoduje to odpowiednie rzutowanie literału.Ale jeśli spróbujesz połączyć dwa literały, kompilator nie będzie w stanie znaleźć operatora, który pobiera dwa literały.
źródło
std::string
z innymstd::string
, tablicą znaków lub pojedynczym znakiem.Twój drugi przykład nie działa, ponieważ nie ma
operator +
dwóch literałów ciągu. Zauważ, że literał ciągu nie jest typustring
, ale zamiast tego jest typuconst char *
. Twój drugi przykład zadziała, jeśli poprawisz go w ten sposób:źródło
Od C ++ 14 możesz używać dwóch prawdziwych literałów łańcuchowych :
lub
źródło
W przypadku 1, ze względu na kolejność operacji otrzymujesz:
(witaj + ", świat") + "!" co zamienia się w hello + „!” i na koniec witam
W przypadku 2, jak zauważył James, otrzymasz:
("Hello" + ", world") + exclam, czyli konkatacja 2 literałów łańcuchowych.
Mam nadzieję, że to jasne :)
źródło
Różnica między łańcuchem znaków (a ściślej mówiąc
std::string
) a literałem znakowym polega na tym, że dla tego drugiego nie ma+
zdefiniowanego operatora. Dlatego drugi przykład zawodzi.W pierwszym przypadku kompilator może znaleźć odpowiedni
operator+
z pierwszym argumentem a,string
a drugim literałem znakowym (const char*
), więc użył tego. Wynikiem tej operacji jest znowu astring
, więc powtarza tę samą sztuczkę podczas dodawania"!"
do niego.źródło