为什么 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 & 获取其参数,而 ints 的流式运算符 << 通过值获取 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:您没有正确调用编译器。