Что такое стрлен-элизион?

avatar
user5560811
8 апреля 2018 в 03:58
447
2
4

Я могу видеть его в списке как одну из современных идиом C++, но что это такое?

Это просто тип copy elision?

Источник
273K
8 апреля 2018 в 04:11
3

Возможный дубликат Оценка strlen во время компиляции? В одном из комментариев есть ответ на ваш вопрос.

user5560811
12 апреля 2018 в 12:35
0

@С.М. нет, это не ответ на мой вопрос. Правильный ответ см. ниже.

Ответы (2)

avatar
Rakete1111
8 апреля 2018 в 04:14
9

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

Но в большинстве случаев компилятор способен оптимизировать <88833333128881> и подсчитать количество символов в самой строке вместо того, чтобы заставлять это делать результирующую программу. Иногда это называется удалением strlen (поскольку при этом игнорируется вызов strlen).

А <88833333128894>простой пример<88833333128895> из <88833333128882> просто будет полностью оптимизирован.<88833333128896>

#include <cstring>

int main() {
    return std::strlen("hi");
}

Под <88833333128883> результирующая сборка

main: # @main
  mov eax, 2
  ret

Даже <88833333128900>под -O0 цикл не создается!

Это довольно простой пример, но он работает даже для немного более сложных случаев использования std::strlen.<88833333128904>

#include <cstring>

int main(int argc, char**) {
    const char *string;
    if (argc == 1)
        string = "hello";
    else if (argc == 2)
        string = "world";
    else if (argc == 3)
        string = "!";

    return std::strlen(string); 
}

Здесь строки полностью оптимизированы, а также вызов std::strlen.<88833333128908>

user5560811
8 апреля 2018 в 05:01
0

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

Rakete1111
8 апреля 2018 в 05:07
1

@DusanJovanovic Не совсем. Если нет задействованных строковых литералов, то компилятор должен узнать, насколько длинна строка на самом деле. Но компилятор всегда может удивить :)

Paul Sanders
8 апреля 2018 в 07:51
2

С «полной оптимизацией программы» (выполняемой компоновщиком Microsoft) возможно многое. Например, если строковый литерал передается функции, которая, в свою очередь, вызывает strlen, и эта функция встраивается компоновщиком, то вызов strlen становится исключаемым. Если компоновщик так же умен, как и компилятор, то есть.

avatar
Paul Sanders
8 апреля 2018 в 04:10
0

При всем уважении к Rakete, мне кажется само собой разумеющимся, что если компилятор понимает семантику strlen и если рассматриваемая строка известна во время компиляции, то компилятор может просто выдать константу, а не вычислять длина строки (любыми способами) во время выполнения. Это все, что здесь происходит. Доверьтесь отделу маркетинга Microsoft, который раскрутит такую ​​простую функцию.

Что интересует меня во всем этом, так это растущая тенденция (оптимизирующих) компиляторов рассматривать часто используемые функции библиотеки времени выполнения как встроенные функции и создавать для них лучший код, когда это возможно. Это происходит уже некоторое время — очевидными примерами являются memcpy и memset — но недавно я играл с Godbolt's Compiler Explorer и был поражен, увидев, что gcc заменил это:

printf (" ");

с этим:

putchar (' ');

В шоке? Я тоже, и я не уверен, что мне это нравится. У меня может быть пользовательский printf, который делает что-то особенное, и такой трюк станет неприятным сюрпризом. Мне кажется большой риск (со стороны авторов компилятора) для такого небольшого выигрыша.

Pete Becker
8 апреля 2018 в 12:37
2

Если у вас есть пользовательский printf, то вы не пишете стандартный C или C++, и вы сами по себе.

Paul Sanders
8 апреля 2018 в 17:53
0

Думаешь? Где в стандарте сказано «не заменяйте стандартную библиотечную функцию» (написав замену и связав ее)? Конечно, для чего-то вроде strlen, который можно рассматривать как встроенный, это крайне неразумно, но для printf? Я действительно думаю, что компилятор должен оставить только вызовы такой сложной функции. Это то, что я пытался подчеркнуть: манипулирование вызовом printf выходит далеко за рамки того, что большинство людей ожидают от компилятора, — не то чтобы я обязательно этого хотел.

Pete Becker
8 апреля 2018 в 19:29
2

Имена стандартных библиотечных функций — это имена с внешней связью. Таким образом, они зарезервированы, и поведение программы, определяющей любое из этих имен, не определено. [зарезервировано.имена]/2.

Chuck Walbourn
9 апреля 2018 в 18:01
0

Другими словами, вы можете написать myprintf, но вы не должны пытаться написать свой собственный printf.

Paul Sanders
9 апреля 2018 в 20:47
0

Хорошо, это достаточно справедливо, и я в любом случае не планировал, но я действительно думаю, что авторы компилятора зашли слишком далеко. Ничего не получится, зачем заморачиваться?

Lothar
17 мая 2020 в 20:01
0

Я почти уверен, что компилятор делает это только тогда, когда вы используете объявление printf из исходного файла заголовка. И этот исходный заголовочный файл будет иметь некоторые прагматические украшения на идентификаторе, поэтому он никогда не спутает его printf с вашим printf. Помните, что стандартные библиотеки заголовков не обязательно должны быть написаны на C++, они могут использовать всю магию, предоставляемую поставщиком компилятора.