Чистые функции / функции константы в C++

Я думаю об использовании чистых функций / функций константы в большей степени в моем коде C++. (чистый атрибут / атрибут константы в GCC)

Однако мне любопытно, насколько строгий я должен быть об этом и что могло возможно повредиться.

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

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

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

Так, для суммирования я хочу отметить функции как чистые / константа, которые не чисты / константа в строгом смысле. Легкий пример:

int foo(int) __attribute__((const));

int bar(int x) {
   int sum = 0;
   for(int i = 0; i < 100; ++i)
       sum += foo(x);
   return sum;
}

int foo_callcounter = 0;

int main() {
   cout << "bar 42 = " << bar(42) << endl;
   cout << "foo callcounter = " << foo_callcounter << endl;
}

int foo(int x) {
   cout << "DEBUG: foo(" << x << ")" << endl;
   foo_callcounter++;
   return x; // or whatever
}

Обратите внимание, что функциональное нечто не является константой в строгом смысле. Хотя, не имеет значения, какой foo_callcounter находится в конце. Также не имеет значения, если оператор отладки не сделан (в случае, если функция не вызвана).

Я ожидал бы вывод:

DEBUG: foo(42)
bar 42 = 4200
foo callcounter = 1

И без оптимизации:

DEBUG: foo(42) (100 times)
bar 42 = 4200
foo callcounter = 100

Оба случая прекрасны полностью, потому что то, что только имеет значение для моего варианта использования, является возвращаемым значением панели (42).

Как это удается на практике? Если я отмечаю такие функции как чистые / константа, она могла бы повредить что-нибудь (полагающий, что код является все правильным)?


Обратите внимание, что я знаю, что некоторые компиляторы не могли бы поддерживать этот атрибут вообще. (BTW., я собираю их здесь.) Я также знаю, как использовать эти атрибуты способом, что код остается портативным (через #defines). Кроме того, все компиляторы, которые интересны мне, поддерживают его в некотором роде; таким образом, я не забочусь о том, если мой код работает медленнее с компиляторами, которые не делают.

Я также знаю, что оптимизированный код, вероятно, будет выглядеть по-другому в зависимости от компилятора и даже версии компилятора.


Очень релевантный также эта статья LWN "Последствия чистых и постоянных функций", особенно глава "Обманов". (Спасибо ArtemGr для подсказки.)

14
задан Community 23 May 2017 в 10:32
поделиться

2 ответа

Я подумываю об усилении использования чистых / константных функций в моем C ++ код.

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

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

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

17
ответ дан 1 December 2019 в 12:51
поделиться

Вы определенно можете нарушить переносимость вашего кода. И зачем вам создавать собственный умный указатель, не считая опыта обучения? Разве их недостаточно для вас в (почти) стандартных библиотеках?

3
ответ дан 1 December 2019 в 12:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: