С++: оптимизирующая функция без побочных эффектов

Есть ли способ в С++ объявить, что функция не имеет побочных эффектов? Рассмотрим:

LOG("message").SetCategory(GetCategory()); 

Теперь предположим, что макрос LOG в релизах создает объект NullLogEntry, который имеет SetCategory(), определенный как пустая функция. Таким образом, в основном все выражение могло бы (и должно) быть оптимизировано прочь - за исключением того, что теоретически вызов GetCategory() может иметь некоторые побочные эффекты, поэтому я думаю, что компилятору не разрешено просто выбросить его.

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

Я прав? Или могут ли компиляторы оптимизировать такие вызовы? Если нет, есть ли способ намекнуть компилятору, что эта функция не имеет побочных эффектов, поэтому, если возвращаемое значение игнорируется, тогда весь вызов может быть пропущен?

Ответ 1

Нет стандартного способа сделать это, но некоторые компиляторы имеют аннотации, которые можно использовать для этого, например, в GCC вы можете использовать тег __attribute_pure__ в функции (альтернативно __attribute__((pure))), чтобы сообщить компилятор, что функция чиста (т.е. не имеет побочных эффектов). Это широко используется в стандартной библиотеке C, так что, например:

char * str = get_some_string();
for ( int i = 0; i < strlen( str ); ++i ) {
    str[i] = toupper(str[i]);
}

Может быть оптимизирован компилятором в:

char * str = get_some_string();
int __length = strlen( str );
for ( int i = 0; i < __length; ++ i ) {
   str[i] = toupper(str[i]);
}

Функция объявляется в заголовке string.h как:

extern size_t strlen (__const char *__s)
     __THROW __attribute_pure__ __nonnull ((1));

Где __THROW - исключение без исключения в случае, если он является компилятором С++, анализирующим функцию, а __nonnull((1)) сообщает компилятору, что первый аргумент не должен быть нулевым (т.е. вызывать предупреждение, если аргумент имеет значение null и Используется флаг Wnonnull).

Ответ 2

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

Чтобы добиться того, чего вы хотите с 100% уверенностью, вам нужно обернуть весь оператор макросом, который будет оценивать пустой оператор для вашей конфигурации выпуска.

Ответ 3

Это проблема с тем, что у вас есть код режима отладки.

Единственное надежное решение (для вызовов функций) - это обернуть весь код отладки внутри самого макроса.

Например, вместо этого вы можете использовать следующий код:

LOG("message", GetCategory());

Затем препроцессор уничтожит все выражение в Release, и вам больше не придется об этом беспокоиться.