Преждевременная оптимизация или действительно ли я являемся сумасшедшими?

Я недавно видел часть кода в comp.lang.c ++ модерируемый возврат ссылки статического целого числа от функции. Код был чем-то вроде этого

int& f()
{
   static int x;
   x++;
   return x;
}

int main()
{
  f()+=1; //A
  f()=f()+1; //B
  std::cout<<f();

}

Когда я отладил приложение с помощью своего прохладного отладчика Visual Studio, я видел всего один вызов к оператору A, и угадайте то, что я был потрясен. Я всегда думал i+=1 было равно i=i+1 так f()+=1 было бы равно f()=f()+1 и я видел бы два вызова к f(), но я видел только один. Какого черта это? Я являюсь сумасшедшим, или мой отладчик сходится с ума, или действительно ли это - результат преждевременной оптимизации?

7
задан casperOne 9 August 2010 в 15:46
поделиться

5 ответов

Это то, что Стандарт говорит о + = и его друзьях:

5.17-7: Поведение выражения формы E1 op = E2 эквивалентно E1 = E1 op E2, за исключением того, что E1 является оценивается только один раз. [...]

Так что компилятор прав в этом.

27
ответ дан 6 December 2019 в 04:51
поделиться

i+=1 - это функционально то же самое, что и i=i+1. На самом деле он реализован по-другому (в основном, он предназначен для использования преимуществ оптимизации на уровне процессора).

Но по сути левая часть оценивается только один раз. Она дает неконстантное l-значение, и это все, что нужно, чтобы прочитать значение, добавить единицу и записать его обратно.

Это более очевидно, когда вы создаете перегруженный оператор для пользовательского типа. operator+= изменяет экземпляр this. operator+ возвращает новый экземпляр. Обычно рекомендуется (в C++) сначала написать oop+=, а затем написать op+ в его терминах.

(Обратите внимание, что это относится только к C++; в C#, op+= - это именно то, что вы предполагали: просто сокращение для op+, и вы не можете создать свой собственный op+=. Он автоматически создается для вас из op+)

.
10
ответ дан 6 December 2019 в 04:51
поделиться

Ваше мышление логично, но неверно.

i += 1;
// This is logically equivalent to:
i = i + 1;

Но логически эквивалентные и тождественные - это не одно и то же.
Код должен выглядеть следующим образом:

int& x = f();
x += x;
// Now you can use logical equivalence.
int& x= f();
x = x + 1;

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

9
ответ дан 6 December 2019 в 04:51
поделиться

f() возвращает ссылку на статическое целое число. Затем += 1 добавляет единицу к этой области памяти - нет необходимости вызывать его дважды в операторе A.

3
ответ дан 6 December 2019 в 04:51
поделиться

На каждом языке, который я видел, который поддерживает оператор + =, компилятор оценивает операнд в левой части один раз, чтобы получить некоторый тип адреса, который затем используется как для чтения старое значение и напишите новое. Оператор + = - это не просто синтаксический сахар; как вы заметили, он может достичь семантики выражения, чего было бы неудобно достичь другими способами.

Между прочим, операторы «With» в vb.net и Pascal имеют схожую функцию. Оператор вроде:

' Assime Foo is an array of some type of structure, Bar is a function, and Boz is a variable.
  With Foo(Bar(Boz))
    .Fnord = 9
    .Quack = 10
  End With
вычислит адрес Foo (Bar (Boz)), а затем установит для двух полей этой структуры значения девять и десять. В C это было бы эквивалентно
  {
    FOOTYPE *tmp = Foo(Bar(Boz));
    tmp->Fnord = 9;
    tmp->Quack = 10;
  }

но vb.net и Паскаль не предоставляют временный указатель. В то время как можно добиться того же эффекта в VB.net без использования «With» для хранения результата Bar (), использование «With» позволяет избежать использования временной переменной.

0
ответ дан 6 December 2019 в 04:51
поделиться
Другие вопросы по тегам:

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