以下代码可在clang ++中工作,但在g ++中会崩溃
#include<vector>
#include<iostream>
template<class Iterator>
double abs_sum(double current_sum, Iterator it, Iterator it_end){
if (it == it_end)
return current_sum;
return abs_sum(current_sum+std::abs(*it),++it,it_end);
}
int main(int argc, char** argv){
std::vector<double> values {1.0, 2.0,-5};
std::cout << abs_sum(0.0,values.begin(),values.end()) << std::endl;;
}
罪魁祸首是这条线:
return abs_sum(current_sum+std::abs(*it),++it,it_end);
在clang中,*it
在++it
之前进行求值,在g ++中则相反,导致迭代器在取消引用之前先增加。事实证明,函数参数的求值顺序是实现定义的。
我的问题是:如何捕获此类错误?理想情况下,当我不小心依赖于实现的具体细节时,我希望有一个错误或至少一个警告。
clang和gcc都不会发出任何警告,即使使用-Wall也是如此。
答案 0 :(得分:4)
我的问题是:如何捕获此类错误?
你不知道。未定义的行为是未定义的。你抓不到...
...但是某些工具可以帮助您:
-Wall -Wextra -pedantic
是一个好的开始); 尽管它们不提供任何保证。这就是为什么C ++很难做到的。您(编码员)最了解并且不编写UB。祝你好运。
答案 1 :(得分:3)
不幸的是,即使使用-Wextra
(请记住,-Wall
更像-Wsome
,因此不够用),对此也没有警告,这有点令人失望。
在一个比较简单的情况下,使用原始语言,其中race *对编译器更明显:
void foo(int, int) {}
int main()
{
int x = 42;
foo(++x, x);
}
…您被警告:
main.cpp: In function 'int main()':
main.cpp:6:9: warning: operation on 'x' may be undefined [-Wsequence-point]
foo(++x, x);
^~~
main.cpp:6:9: warning: operation on 'x' may be undefined [-Wsequence-point]
(*不是真正的种族,但您知道我的意思)
但是对于编译器来说,“知道”您在迭代器上的操作分别是读取和写入是非常困难的。
最终,恐怕您将不得不依靠测试,智慧和勇气。 :)
答案 2 :(得分:2)
您最初拥有的不是未定义的行为,而是未指定的行为。不需要编译器为未指定的行为发出任何诊断。
Order of evaluation几乎所有C ++运算符的操作数(包括函数调用表达式中函数参数的求值顺序和任何表达式中子表达式的求值顺序) 未指定。编译器可以按任何顺序求值操作数,并且在再次求同一个表达式时可以选择其他顺序。
但是在这种情况下,这种未指定行为的结果导致对结束迭代器的取消引用,进而导致未定义行为。
GCC和Clang没有任何常规的编译器选项可以发出针对未指定行为的诊断。
在GCC中,选项fstrong-eval-order
执行以下操作:
按照C ++ 17的要求,以从左到右的顺序评估成员访问,数组下标和移位表达式,并以从右到左的顺序评估赋值。默认启用
-std=c++17
。-fstrong-eval-order=some
仅启用成员访问和移位表达式的排序,这是不包含-std=c++17
的默认设置。
还有-Wreorder
选项(仅适用于C ++和Objective-C ++):
当代码中给定的成员初始化程序的顺序与执行它们的顺序不匹配时发出警告
但是我认为这些选项对您的具体情况没有帮助。
因此,在这种特殊情况下,您可以按预期的顺序执行操作。