为什么 int a; a = std::max(a, x) 不发出 "uninitialized" 警告
Why int a; a = std::max(a, x) doesn't emit "uninitialized" warnings
考虑以下代码:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v{{1, 2, 3}};
int a;
std::cout << a << std::endl; // 1
for (const int x : v) {
a = std::max(a, x); // 2
}
std::cout << a << std::endl;
return 0;
}
随着现代编译器的发展,现在对愚蠢的程序员错误保持警惕,他们跟踪单元化变量。然而,这段 C++ 代码让他们感到困惑。到目前为止,我得到以下结果:
(1) (2)
g++ 5.3.1
clang++ 3.7 ✔
Solaris Studio 12.5 ✔
如您所见,CLang 和 solstudio 只能检测情况 (1) 并忽略情况 (2),而 g++ 会忽略两者。在情况(2)中是否存在检测它的并发症?为什么 g++ 在这方面如此糟糕?
我使用的编译器选项:
$ g++-5 -std=c++11 -Wall -Wpedantic -pedantic -Wextra \
-Wuninitialized -Wmaybe-uninitialized aisa.cpp
$ clang++ -std=c++11 -Wall -Wpedantic -pedantic -Wextra -Wuninitialized aisa.cpp
$ CC -std=c++11 -xprevise aisa.cpp
std::max
通过 const &
获取其参数,而 int
s 的流式运算符 <<
通过值获取 int
。通过引用传递未初始化的对象是合法的:例如,如果函数只获取其地址,则一切正常。因此,将 a
传递给 std::max
的警告很容易成为误报。
首先:两个编译器都只诊断初犯,即它们只报告第一次未初始化的使用a
。所以要得到第二个警告,我们需要删除第一行:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v{{1, 2, 3}};
int a;
for (const int x : v) {
a = std::max(a, x); // 2
}
std::cout << a << std::endl;
return 0;
}
现在我们看到两个不相关的编译器怪癖:clang 不在 -Wall
和 -Wextra
中包含 -Wconditional-uninitialized
。如果你启用它,你 do 会在 std::cout
处收到警告,因为它可能会打印一个未初始化的变量。
另一方面,gcc 仅在启用优化器时跟踪未初始化的变量,可能是为了加快调试版本的编译速度。对于 -O2 -Wall
,gcc 6 在这两种情况下都会发出警告,但不会像 clang 在第二种情况下那样精确定位位置。 (gcc <= 5.3 不会像您观察到的那样警告第二种情况,所以它似乎是最近实施的。)
所以 TL;DR:您没有正确调用编译器。
考虑以下代码:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v{{1, 2, 3}};
int a;
std::cout << a << std::endl; // 1
for (const int x : v) {
a = std::max(a, x); // 2
}
std::cout << a << std::endl;
return 0;
}
随着现代编译器的发展,现在对愚蠢的程序员错误保持警惕,他们跟踪单元化变量。然而,这段 C++ 代码让他们感到困惑。到目前为止,我得到以下结果:
(1) (2)
g++ 5.3.1
clang++ 3.7 ✔
Solaris Studio 12.5 ✔
如您所见,CLang 和 solstudio 只能检测情况 (1) 并忽略情况 (2),而 g++ 会忽略两者。在情况(2)中是否存在检测它的并发症?为什么 g++ 在这方面如此糟糕?
我使用的编译器选项:
$ g++-5 -std=c++11 -Wall -Wpedantic -pedantic -Wextra \
-Wuninitialized -Wmaybe-uninitialized aisa.cpp
$ clang++ -std=c++11 -Wall -Wpedantic -pedantic -Wextra -Wuninitialized aisa.cpp
$ CC -std=c++11 -xprevise aisa.cpp
std::max
通过 const &
获取其参数,而 int
s 的流式运算符 <<
通过值获取 int
。通过引用传递未初始化的对象是合法的:例如,如果函数只获取其地址,则一切正常。因此,将 a
传递给 std::max
的警告很容易成为误报。
首先:两个编译器都只诊断初犯,即它们只报告第一次未初始化的使用a
。所以要得到第二个警告,我们需要删除第一行:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v{{1, 2, 3}};
int a;
for (const int x : v) {
a = std::max(a, x); // 2
}
std::cout << a << std::endl;
return 0;
}
现在我们看到两个不相关的编译器怪癖:clang 不在 -Wall
和 -Wextra
中包含 -Wconditional-uninitialized
。如果你启用它,你 do 会在 std::cout
处收到警告,因为它可能会打印一个未初始化的变量。
gcc 仅在启用优化器时跟踪未初始化的变量,可能是为了加快调试版本的编译速度。对于 -O2 -Wall
,gcc 6 在这两种情况下都会发出警告,但不会像 clang 在第二种情况下那样精确定位位置。 (gcc <= 5.3 不会像您观察到的那样警告第二种情况,所以它似乎是最近实施的。)
所以 TL;DR:您没有正确调用编译器。