Зачем допускать конкатенацию строковых литералов?

Недавно меня укусила тонкая ошибка.

char ** int2str = {
   "zero", // 0
   "one",  // 1
   "two"   // 2
   "three",// 3
   nullptr };

assert( int2str[1] == std::string("one") ); // passes
assert( int2str[2] == std::string("two") ); // fails

Если у вас есть божественная проверка кода, вы заметите, что я забыл , после "two".

После значительных усилий, чтобы найти эту ошибку, я должен спросить, почему кто-либо когда-либо захочет этого поведения?

Я вижу, как это может быть полезно для макромагии, но почему же это "особенность" на современном языке, таком как python?

Вы когда-нибудь использовали конкатенацию строк в производственном коде?

Ответ 1

Я вижу несколько ответов на C и С++, но ни один из них действительно не отвечает, почему и на самом деле, что послужило основанием для этой функции? В С++ эта функция исходит из C99, и мы можем найти обоснование этой функции, перейдя в Обоснование для языков международного стандартного программирования-C 6.4.5 Строковые литералы, в которых говорится (акцент мой):

Строка может быть продолжена на нескольких строках с использованием продолжения линии обратной косой черты, но для этого требуется, чтобы продолжение строки начиналось в первой позиции следующей строки. Чтобы разрешить более гибкий макет, и для решения некоторых проблем с предварительной обработкой (см. раздел 6.10.3), Комитет C89 представил строковое литературное конкатенацию. Два строковых литерала подряд склеиваются вместе, без нулевого символа в середине, чтобы создать один комбинированный строковый литерал. Это дополнение к языку C позволяет программисту расширять строковый литерал за пределами физической строки без использования механизма обратной косой черты и тем самым уничтожить схему отступа программы. Явный оператор конкатенации не был введен, поскольку конкатенация является лексической конструкцией, а не операцией времени выполнения.

Python, который, похоже, имеет одну и ту же причину, уменьшает необходимость уродливого \ для продолжения длинных строковых литералов. Это описано в разделе 2.4.2 Строковое литерала конкатенации Справочник по языку Python.

Ответ 2

Конечно, это простой способ сделать ваш код хорошим:

char *someGlobalString = "very long "
                         "so broken "
                         "onto multiple "
                         "lines";

Лучшая причина, однако, в том, что для странных форматов printf, таких как принудительное форматирование:

uint64_t num = 5;
printf("Here is a number:  %"PRIX64", what do you think of that?", num);

Есть определенная группа, и они могут пригодиться, если у вас есть требования к размеру шрифта. Проверьте их все по этой ссылке. Несколько примеров:

PRIo8 PRIoLEAST16 PRIoFAST32 PRIoMAX PRIoPTR

Ответ 3

Это отличная функция, которая позволяет комбинировать строки препроцессора со строками.

// Here we define the correct printf modifier for time_t
#ifdef TIME_T_LONG
    #define TIME_T_MOD "l"
#elif defined(TIME_T_LONG_LONG)
    #define TIME_T_MOD "ll"
#else
    #define TIME_T_MOD ""
#endif

// And he we merge the modifier into the rest of our format string
printf("time is %" TIME_T_MOD "u\n", time(0));

Ответ 4

Случаи, где это может быть полезно:

  • Генерация строк, включая компоненты, определенные препроцессором (это, пожалуй, самый большой вариант использования в C, и я вижу очень, очень часто).
  • Разделение строковых констант на несколько строк

Чтобы предоставить более конкретный пример для первого:

// in version.h
#define MYPROG_NAME "FOO"
#define MYPROG_VERSION "0.1.2"

// in main.c
puts("Welcome to " MYPROG_NAME " version " MYPROG_VERSION ".");

Ответ 5

Из справочника лексического анализа питона, раздел 2.4.2:

Эта функция может использоваться для уменьшения количество требуемых обратных косых черт, разделение длинные строки удобно строк или даже добавлять комментарии к части строк

http://docs.python.org/reference/lexical_analysis.html

Ответ 6

Я не уверен в других языках программирования, но, например, С# не позволяет вам это делать (и я думаю, что это хорошо). Насколько я могу судить, большинство примеров, которые показывают, почему это полезно в С++, по-прежнему будут работать, если вы можете использовать какой-то специальный оператор для конкатенации строк:

string someGlobalString = "very long " +
                          "so broken " +
                          "onto multiple " +
                          "lines"; 

Это может быть не так удобно, но это, безусловно, безопаснее. В вашем мотивирующем примере код будет недействительным, если вы не добавили либо , для разделения элементов, либо +, чтобы объединить строки...

Ответ 7

Итак, вы можете разделить длинные строковые литералы по строкам.

И да, я видел это в производственном коде.

Ответ 8

Для обоснования, расширения и упрощения ответа Шафика Ягмура: строковая литературная конкатенация, возникшая в C (следовательно, унаследованная С++), как и этот термин, по двум причинам (ссылки из Обоснование для языка программирования ANSI C.:

  • Для форматирования: чтобы длинные строковые литералы охватывали несколько строк с правильным отступом - в отличие от продолжения строки, которая разрушает схему отступа (3.1.4 String литералов); и
  • Для макромагии: разрешить построение строковых литералов макросами (через строкование) (3.8.3.2 Оператор #).

Он включен в современные языки Python и D, потому что они скопировали его из C, хотя в обоих из них оно было предложено для устаревания, поскольку оно подвержено ошибкам (как вы заметили) и ненужно (поскольку можно просто есть оператор конкатенации и постоянная сворачивание для оценки времени компиляции, вы не можете сделать это на C, потому что строки являются указателями, и поэтому вы не можете их добавить).

Его не просто удалить, поскольку это нарушает совместимость, и вы должны быть осторожны в отношении приоритета (неявное конкатенация происходит во время лексинга, до операторов, но заменяя это оператором, значит, вам нужно быть осторожным с приоритетом), поэтому его все еще присутствует.

Да, это код используемого продукта. Руководство по стилю Google Python: Длина строки указывает:

Когда литеральная строка не будет помещаться в одну строку, используйте круглые скобки для неявного объединения строк.

x = ('This will build a very long long '
     'long long long long long long string')

Смотрите "String literal concatenation "в Википедии для получения более подробной информации и ссылок.

Ответ 9

Я, конечно, имею и в C и С++. Оффлайн, я не вижу большой зависимости между его полезностью и тем, насколько "современным" является язык.

Ответ 10

В то время как люди изъяли слова из-за практического использования этой функции, никто до сих пор не пытался защитить выбор синтаксиса.

Насколько мне известно, опечатка, которая может проскользнуть в результате, вероятно, просто упускается из виду. В конце концов, кажется, что устойчивость к опечаткам не была на виду у Денниса, как показано далее:

if (a = b);
{
    printf("%d", a);
}

Кроме того, существует возможное представление о том, что не стоит использовать лишний символ для конкатенации строковых литералов. В конце концов, нет ничего другого, что можно сделать с двумя из них, и иметь символ там может создайте соблазн попытаться использовать его для конкатенации строки выполнения, которая находится выше уровня встроенных функций C.

Некоторые современные языки более высокого уровня, основанные на синтаксисе Си, отбросили эту нотацию, предположительно, потому что она подвержена ошибкам. Но у этих языков есть оператор для конкатенации строк, например + (JS, С#), . (Perl, PHP), ~ (D, хотя это также поддерживало синтаксис сопоставления Си) и постоянную фальцовку ( в компилируемых языках, в любом случае) означает, что накладные расходы на выполнение не выполняются.