Стандарты программирования на С++. 101 правило и рекомендация | страница 54
>int i = 0;
>f(i++), g(i); //См. также рекомендацию 31
Пример. Инициализация библиотеки при помощи перегруженного оператора >operator,
для последовательности инициализаций. Некоторая библиотека пытается упростить добавление нескольких значений в контейнер за один раз путем перегрузки оператора-запятой. Например, для добавления в >vector
:
>set_cont(letters) += "a", "b";
Все в порядке, пока в один прекрасный день пользователь не напишет:
>set_cont(letters) += getstr(), getstr();
>// порядок не определен при использовании
>// перегруженного оператора ","
Если функция >getstr
получает, например, ввод пользователя и он введет строки >"с"
и >"d"
в указанном порядке, то в действительности строки могут оказаться внесены в любом порядке. Это может оказаться сюрпризом, поскольку при использовании встроенного оператора >operator,
такой проблемы не возникает:
>string s; s = getstr(), getstr(); // порядок строго определен
>// при использовании
>// встроенного оператора ","
Исключение — специализированные библиотеки шаблонов для научных вычислений, которые в соответствии с дизайном переопределяют все операторы.
[Dewhurst03] §14 • [Meyers96] §7, §25 • [Murray93] §2.4.3 • [Stroustrup00] §6.2.2
31. Не пишите код, который зависит от порядка вычислений аргументов функции
Порядок вычисления аргументов функции не определен, поэтому никогда не полагайтесь на то, что аргументы будут вычисляться в той или иной очередности.
На начальных этапах развития языка C регистры процессора были драгоценным ресурсом и компиляторы решали трудные задачи эффективного их использования в сложных выражениях высокоуровневых языков. Для того чтобы позволить компилятору генерировать более быстрый код, создатели C дали распределителю регистров дополнительную степень свободы. При вызове функции порядок вычисления ее аргументов оставался неопределенным. Эта аргументация, вероятно, существенно менее важна в настоящее время, но главное, что порядок вычисления аргументов функций в C++ не определен и варьируется от компилятора к компилятору (см. также рекомендацию 30).
В связи с этим необдуманные действия программиста могут привести к большим неприятностям. Рассмотрим следующий код:
>void Transmogrify(int, int);
>int count = 5;
>Transmogrify(++count, ++count); // Порядок вычислений
> // неизвестен
Все, что мы можем сказать определенного, — это то, что при входе в тело функции