Объедините два строковых литерала

121

Я читаю Accelerated C ++ от Koenig. Он пишет, что «новая идея состоит в том, что мы можем использовать + для объединения строки и строкового литерала - или, если на то пошло, двух строк (но не двух строковых литералов).

Хорошо, я полагаю, это имеет смысл. Теперь о двух отдельных упражнениях, призванных прояснить это.

Верны ли следующие определения?

const string hello = "Hello";

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

Теперь я попытался выполнить описанное выше, и это сработало! Так что я был счастлив.

Затем я попытался сделать следующее упражнение;

const string exclam = "!";

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

Это не сработало. Теперь я понимаю, что это как-то связано с тем фактом, что вы не можете объединить два строковых литерала, но я не понимаю семантической разницы между тем, почему мне удалось заставить работать первый пример (это не «, world» и «! «два строковых литерала? Разве это не должно было работать?), но не второй.

Артур Колле
источник
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;

+Оператор слева на правую ассоциативность, так что эквивалентно в скобках выражение:

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

Как видите, сначала «добавляются» два строковых литерала "Hello"и ",world", отсюда и ошибка.

Одна из первых двух конкатенируемых строк должна быть 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*вместе, что невозможно (что бы значило сложить два указателя вместе?), И если бы это было так, то не получилось бы того, что вы хотел это сделать.


Обратите внимание, что вы можете объединить строковые литералы, поместив их рядом друг с другом; например, следующие два эквивалентны:

"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"синтаксис полезен не только для разбиения на несколько строк, но и когда один из строковых литералов является макросом (или даже обоими). Затем конкатенация происходит во время компиляции.
Melebius
8

Всегда следует обращать внимание на типы .

Хотя все они кажутся строками, "Hello"а ",world"являются литералами .

А в вашем примере exclamэто std::stringобъект.

В C ++ есть перегрузка оператора, которая берет std::stringобъект и добавляет к нему другую строку. Когда вы объединяете std::stringобъект с литералом, он выполняет соответствующее приведение для литерала.

Но если вы попытаетесь объединить два литерала, компилятор не сможет найти оператор, который принимает два литерала.

Йохай Тиммер
источник
См. Std :: operator +, который предлагает перегрузки для объединения a std::stringс другим std::string, массивом символов или одиночным символом.
DavidRR
7

Ваш второй пример не работает, потому что нет operator +двух строковых литералов. Обратите внимание, что строковый литерал имеет не тип string, а тип const char *. Ваш второй пример будет работать, если вы измените его следующим образом:

const string message = string("Hello") + ",world" + exclam;
Джурадж Блахо
источник
4

Начиная с C ++ 14 вы можете использовать два реальных строковых литерала :

const string hello = "Hello"s;

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

или

const string exclam = "!"s;

const string message = "Hello"s + ",world"s + exclam;
Томас Саблик
источник
2

В случае 1 из-за порядка операций вы получаете:

(привет + ", мир") + "!" который преобразуется в hello + "!" и, наконец, привет

В случае 2, как заметил Джеймс, вы получите:

("Привет" + ", мир") + восклицательный знак, который является объединением двух строковых литералов.

Надеюсь, все понятно :)

Стивен
источник
1

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

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

Петер Торок
источник