Порядок вычисления выражений C ++ - PullRequest
4 голосов
/ 21 января 2010

Я столкнулся с любопытной проблемой, связанной с оценкой выражений:

reference operator()(size_type i, size_type j) {
  return by_index(i, j, index)(i, j); // return matrix index reference with changed i, j
}

matrix& by_index(size_type &i, size_type &j, index_vector &index) {
  size_type a = position(i, index); // find position of i using std::upper_bound
  size_type b = position(j, index);
  i -= index[a];
  j -= index[b];
  return matrix_(a,b); // returns matrix reference stored in 2-D array
}

Я думал, что матрица (i, j) будет оценена после вызова buy_index, так что i, j будет обновлено. это кажется правильным, я проверен в отладчике. однако для некоторых типов матриц, в частности тех, которые должны приводить size_type к чему-то другому, например, int, обновление в by_index теряется. изменение кода немного устраняет проблему:

reference operator()(size_type i, size_type j) {
  matrix &m = by_index(i, j, index);
  return m(i, j); 
}

Вы знаете, почему первый оператор плохо себя ведет? спасибо

прототипы, которые работают, а которые нет

inline reference operator () (size_t i, size_t j); // ublas, size_type is std::size_t
reference operator () (int i, int j); // other prototype, size_type is int

в стеке трассировки отладчика выглядит так:

  • i = 1 при входе в operator () // все в порядке
  • i = 0 после финиша от by_index // окей
  • i = 1 при входе в matrix :: operator () // не верно, должно быть 0

Ответы [ 5 ]

3 голосов
/ 21 января 2010

На мой взгляд, это сводится к порядку оценки.

Стандарт гласит -

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

Что точно соответствует требованиям. Значения i и j могут быть оценены до вызова by_index () или после него. Вы не можете сказать - это не определено.

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

2 голосов
/ 21 января 2010

Наконец я нашел, где в стандарте это указано (черновик n1905):

(5.2.2-8) - Порядок оценки аргументов не указан. Все побочные эффекты при оценке выражений аргументов вступают в силу до входа в функцию. Порядок вычисления выражения post-x и списка выражений аргументов не указан.

Упомянутое постфиксное выражение - это часть слева от (). Таким образом, во внешнем вызове функции не указывается, если by_index(i, j, index) или ее аргументы (i, j) вычисляются первыми.

Существует точка последовательности после возврата функции, поэтому, когда by_index(i, j, index) возвращает, все побочные эффекты завершены, но параметры (i, j), возможно, уже были оценены (и значения были сохранены в регистре или sth.) До эта функция даже вызывается.

2 голосов
/ 21 января 2010

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

Вы можете попробовать скомпилировать с -fno-strict-aliasing (или его эквивалентом), чтобы отключить эти оптимизации и посмотреть, улучшит ли это ситуацию.

0 голосов
/ 14 июля 2011

В качестве дополнительного замечания, это типичный случай, когда краткость отменяет ясность, чего Брайан Керниган настоятельно советует нам избегать (он написал превосходную книгу по этим вопросам «Практика программирования»). Порядок оценки недостаточно четко определен в таком коде, что приводит к «побочному эффекту» непредсказуемых результатов. Внесенное вами изменение является рекомендуемым подходом к подобным ситуациям.

0 голосов
/ 21 января 2010

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

В любом случае, если вы звоните by_index(size_t &,… с аргументом int, вы берете ссылку на временный объект. Это должно быть предупреждением, но, возможно, вы используете более старый компилятор. Попробуйте явное приведение.

reinterpret_cast< size_t & >( i ) /* modify i before next fn call */

Но, конечно, это не гарантирует, что вы поступите правильно. На самом деле, вам нужно отсортировать неподписанные от подписанных и не предполагать sizeof(int) == sizeof(size_t), потому что часто это не так.

...