Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why int a; a = std::max(a, x) doesn't emit "uninitialized" warnings

Consider the following code:

#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;
}

As modern compilers grew and now keep a watchful eye over dumb programmer mistakes, they track unitialized variables. This C++ code however, confuses them. So far, I get following results:

                        (1)      (2)
g++ 5.3.1
clang++ 3.7              ✔
Solaris Studio 12.5      ✔

As you can see, CLang and solstudio can detect only case (1) and ignore case (2), while g++ is ignoring both. Is there a complication to detect it in case (2)? Why g++ is so bad at this?

Compiler options I used:

$ 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
like image 389
myaut Avatar asked Feb 08 '17 16:02

myaut


2 Answers

std::max takes its arguments by const &, while the streaming operator << for ints takes the int by value. Passing an uninitialised object by reference is legal: for example, if the function just takes its address, all is well. Therefore, warning on passing a to std::max could easily be a false positive.

like image 193
Angew is no longer proud of SO Avatar answered Oct 26 '22 23:10

Angew is no longer proud of SO


First of all: Both compilers only diagnose the first offense, that is they only report the first uninitialized use of a. So to get a warning for the second one, we need to remove that first line:

#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;

}

Now we see two unrelated compiler quirks: clang does not include -Wconditional-uninitialized in -Wall and -Wextra. If you enable that, you do get a warning at the std::cout because it potentially prints an uninitialized variable.

gcc on the other hand only tracks uninitialized variables when the optimizer is enabled, probably to speed up compilation of debug builds. With -O2 -Wall, gcc 6 throws a warning in both cases, though without pinpointing the location as exactly as clang does in the second case. (gcc <= 5.3 does not warn about the second case as you observed, so it seems that was implemented recently.)

So TL;DR: You did not invoke your compilers right.

like image 37
Baum mit Augen Avatar answered Oct 26 '22 23:10

Baum mit Augen