在参数评估顺序中警告UB



我最近在类似的代码中遇到了一个错误

class C
{
public:
// foo return value depends on C's state
// AND each call to foo changes the state.
int foo(int arg) /*foo is not const-qualified.*/ {}
private:
// Some mutable state
};
C c;
bar(c.foo(42), c.foo(43))

最后一个调用在不同的平台上表现不同(由于未定义参数求值顺序,这是完全合法的(,我修好了程序错误。

但其余的代码库很大,我想发现所有其他这种类型的UB。

GCC、Clang或MSVS中是否有针对此类情况的特殊编译器警告?

防止此类错误的理想化和轻量级方法是什么?

参数顺序求值是未指定,而不是未定义。

几乎所有C++运算符的操作数的求值顺序(包括函数调用表达式中函数参数的求值顺序和任何表达式中子表达式的求值顺序(未指定。编译器可以按任何顺序计算操作数,并可以在再次计算同一表达式时选择另一个顺序。

由于它是未指定的行为,而不是未定义的行为,因此编译器不需要为它发布诊断。

GCC和Clang没有任何通用的编译器选项来为未指定的行为发出诊断。

在GCC中,有一个选项fstrong-eval-order可以做到这一点:

按照从左到右的顺序评估成员访问、数组下标和移位表达式,并按照从右到左的顺序评估赋值,如C++17所采用的。默认情况下使用-std=c++17启用。-fstrong-eval-order=some仅启用成员访问和移位表达式的排序,并且是不使用-std=c++17的默认值。

还有选项-Wreorder(仅限C++和Objective-C++(可以做到这一点:

当代码中给定的成员初始化程序的顺序与它们必须执行的顺序不匹配时发出警告

但我认为这些选项对您的特定情况没有帮助。

在下面的语句中,如果您希望在第二个参数之前评估第一个参数:

bar(c.foo(42), c.foo(43))

简单的方法是先将c.foo(42)c.foo(43)的结果存储在中间变量中,然后调用bar()。(关闭编译器优化以避免编译器对语句进行任何重新排序!!(

auto var1 = c.foo(42);
auto var2 = c.foo(43);
bar(var1, var2);

我想你一定就是这样修复这个错误的。

最新更新