C ++表达式评估顺序

时间:2010-01-21 04:00:43

标签: c++ evaluation

我遇到了一个关于表达式评估的奇怪问题:

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
}

我认为在调用buy_index之后将评估矩阵(i,j),以便更新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
调试器中的

backtrace堆栈如下所示:

  • i = 1,进入operator()// okay
  • 从by_index // okay 完成后,
  • i = 0 进入matrix :: operator()// i = 1,应为0

5 个答案:

答案 0 :(得分:3)

在我看来,这归结为评估顺序。

标准说 -

  

(5.4)除非另有说明,否则单个操作员的操作数和个别表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的。

这完全符合法案。可以在调用by_index()之前或之后评估i和j的值。你无法分辨 - 这是未指明的。

我会补充一点,解决你的问题的形式在我的眼中是更具可读性,无论第一种形式是否正确,我都会使用它...

答案 1 :(得分:2)

我怀疑对不同类型的引用是否会引发strict aliasing rules,编译器会使用它来更有效地进行优化。你有两个不同类型的变量/引用,并且编译器假定它们不引用相同的内存(但它们实际上是这样做的)。然后,编译器在错误的假设下优化代码,从而产生错误的结果。

您可以尝试使用-fno-strict-aliasing(或等效的)进行编译以禁用这些优化,看看它是否能改善这种情况。

答案 2 :(得分:2)

最后,我发现标准中指定的位置(n1905草案):

  

(5.2.2-8) - 参数的评估顺序未经指定。参数表达式求值的所有副作用在输入函数之前生效。 post fi x表达式和参数表达式列表的评估顺序未经指定。

提到的后缀表达式()左侧的部分。因此,在“外部”函数调用中,如果首先评估by_index(i, j, index)或它的参数(i, j),则不会指定它。

函数返回后有一个序列点,所以当by_index(i, j, index)返回时,所有副作用都已完成,但(i, j)参数可能已经被评估过(并且值已存储在寄存器或......之前,这个功能甚至被召唤。

答案 3 :(得分:0)

通过这样的参数列表传递数据是非常非常不清楚的。依赖于引用类型与不同大小的基数之间的隐式转换是非常非常不安全的。

无论如何,如果您使用by_index(size_t &,…参数调用int,那么您将获得临时参考。这应该是一个警告,但也许你是一个较旧的编译器。尝试一下明确的演员。

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

但当然不能保证做正确的事。实际上,您需要从签名中对未签名的人进行整理,而不是假设为sizeof(int) == sizeof(size_t),因为通常情况并非如此。

答案 4 :(得分:0)

作为补充说明,这是典型的案例,其中简明扼要的清晰度,Brian Kernighan强烈建议我们避免的事情(他写了一本关于这些问题的优秀书籍,“编程实践”)。评估顺序在这样的代码中没有很好地定义,导致不可预测结果的“副作用”。您所做的更改是针对此类情况的推荐方法。