Об'єднайте два рядкові літерали


121

Я читаю прискорений C ++ від Koenig. Він пише, що "нова ідея полягає в тому, що ми можемо використовувати + для об'єднання рядка і літерального рядка - або, з цього приводу, двох рядків (але не двох рядкових літералів).

Чудово, я думаю, це має сенс. Тепер на дві окремі вправи, призначені для висвітлення цього.

Чи справедливі наступні визначення?

const string hello = "Hello";

const string message = hello + ",world" + "!";

Тепер я спробував виконати вищесказане, і це спрацювало! Тож я був щасливий.

Потім я спробував зробити наступну вправу;

const string exclam = "!";

const string message = "Hello" + ",world" + exclam;

Це не вийшло. Тепер я розумію, що це має щось спільне з тим, що ви не можете об'єднати два рядкові літерали, але я не розумію семантичної різниці між тим, чому мені вдалося отримати перший приклад для роботи (не ", світ" і "! "два рядкові літерали? Не повинно це працювати?), але не другий.


4
const string message = "Hello" ",world" + exclam(напр., опускаючи перше +), треба добре працювати.
n0rd

1
@Joe - Чому б хто писав, "Hello" + ", world!"коли ти можеш це зробити "Hello, world!". Як звичайно, C ++ має приголомшливий і простий спосіб вирішення проблеми. :-)
Бо Перссон

2
@Bo Єдине, про що я можу придумати, це якщо ви використаєте визначення (#define)
Джо Філіпс,

@Joe Навіть тоді ви швидше пишете "Hello" ", world!"(без цього +). Існує ряд скарг на C ++, але я не думаю, що це обробка тут одна з них. Це абсолютно те саме, що ви писали 1 / 3 + 1.5, і скаржилися, оскільки поділ був цілісним поділом. На краще чи на гірше, так працює більшість мов.
Джеймс Канзе

2
@Bo Persson Насправді ця функція "hello" " world" == "hello world"корисна, якщо вам потрібно написати довгий рядок і не хочете, щоб він виходив із вашого вікна, або ви хочете знаходитись у певній лінії звуження. Або якщо один з рядків визначений в макросі.
Димитър Славчев

Відповіді:


140
const string message = "Hello" + ",world" + exclam;

The +Оператор зліва на праву асоціативність, так що еквівалентно в дужках вираз:

const string message = (("Hello" + ",world") + exclam);

Як бачимо, два рядкових літерали "Hello" і ",world""додаються" спочатку, звідси і помилка.

Один з перших двох об'єднаних рядків повинен бути a std::string об'єктом:

const string message = string("Hello") + ",world" + exclam;

Як варіант, ви можете примусити друге + оцінювати першу, скориставшись дужкою тієї частини виразу:

const string message = "Hello" + (",world" + exclam);

Має сенс, що ваш перший приклад ( hello + ",world" + "!") працює, тому що std::string( hello) є одним із аргументів у крайній лівій частині +. Це +оцінюється, результат є std::stringоб'єктом зв'язаного рядка і тим, що виходитьstd::string потім об'єднується з "!".


Що стосується того, чому ви не можете об'єднати два рядкові літерали, використовуючи +це, тому що рядковий літерал - це лише масив символів (const char [N] де Nдовжина рядка плюс один, для нульового термінатора). Коли ви використовуєте масив у більшості контекстів, він перетворюється у вказівник на його початковий елемент.

Отже, коли ви намагаєтеся зробити "Hello" + ",world", те, що ви насправді намагаєтеся зробити, це додати дваconst char* s разом, що неможливо (що це означатиме додавання двох покажчиків разом?), І якщо це не було б робити те, що ви хотів це зробити.


Зауважте, що ви можете об'єднати літеральні рядки, розмістивши їх поруч; наприклад, наступні два еквівалентні:

"Hello" ",world"
"Hello,world"

Це корисно, якщо у вас є довгий буквальний рядок, який потрібно розбити на кілька рядків. Однак вони повинні бути рядковими літералами: це не працюватиме з const char*покажчиками чи const char[N]масивами.


3
Також const string message = "Hello" + (",world"+exclam);буде працювати також через явну дужку (це слово?).
Чінмай Канчі

1
Може бути ще більш повним, якщо ви вкажете, чому працює перший приклад:const string message = ((hello + ",world") + "!");
Марк Викуп

Дякую! Я підозрював, що це пов'язане з асоціативністю ліворуч на право, але не був впевнений, і ця семантична різниця не мала для мене великого сенсу. Я ціную відповідь!
Артур Колле

2
Я зазначу, що "Hello" ",world"синтаксис є корисним не лише для розбиття на кілька рядків, але й тоді, коли один із рядкових літералів є макросом (або навіть обом). Потім конкатенація відбувається за час компіляції.
Мелебій

8

Ви завжди повинні звертати увагу на типи .

Хоча всі вони здаються струнними "Hello"і ",world"є буквальними .

І у вашому прикладі exclamє std::stringоб’єктом.

C ++ має перевантаження оператора, який приймає std::stringоб'єкт і додає до нього ще одну рядок. Коли ви об'єднаєте std::stringоб'єкт з літералом, він зробить відповідний кастинг для літералу.

Але якщо ви спробуєте об'єднати два літерали, компілятор не зможе знайти оператора, який приймає два літерали.


Дивіться std :: operator +, який пропонує перевантаження для об'єднання std::stringз іншим std::string, символьним масивом або одним символом.
DavidRR

7

Ваш другий приклад не працює, оскільки operator +для двох рядкових літералів немає. Зауважте, що літеральний рядок не має типу string, а натомість має тип const char *. Ваш другий приклад спрацює, якщо ви переглянете його так:

const string message = string("Hello") + ",world" + exclam;


2

У випадку 1 через порядок операцій ви отримуєте:

(привіт + ", світ") + "!" який вирішує привіт + "!" і нарешті привіт

У випадку 2, як зазначив Джеймс, ви отримуєте:

("Привіт" + ", світ") + вигук, що є конфатом двох рядкових літералів.

Сподіваюся, це зрозуміло :)


1

Різниця між рядком (або якщо бути точним std::string) і символьним буквалом полягає в тому, що для останнього не +визначений оператор. Ось чому другий приклад не вдається.

У першому випадку компілятор може знайти підходящий, operator+перший аргумент - a, stringа другий - літеральний символ ( const char*), тому він це використав. Результатом цієї операції є знову "a" string, тому він повторює той самий трюк, додаючи "!"його.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.