Why does this initialization from itself not generate compiler warning?

Viewed 54

In this example I have two classes: Test and Test2. Both of their constructors erroneously initialze member variable "val_" from itself, rather than from parameter "val" as intended. The difference is that Test initializes using {} syntax whereas Test2 initializes using () syntax. Only Test2's initialization generates an "initialized with itself" warning.

I'm compiling with -Wall which implies -Winit-from-self. As can be seen from the results printed, both constructors print the wrong value for _val.

909> cat initSelfTest.cc
#include <iostream>

using namespace std;

class Test {
public:
    Test (int val);
private:
    int val_;
};
Test::Test (int val) 
    : val_ {val_}
{
    cerr << "Test::Test; val = " << val << "; val_ = " << val_ << "\n";
}

class Test2 {
public:
    Test2 (int val);
private:
    int val_;
};
Test2::Test2 (int val) 
    : val_ (val_)
{
    cerr << "Test2::Test2; val = " << val << "; val_ = " << val_ << "\n";
}

int main (int argc, char **argv) {
    Test test {781981};
    Test2 test2 {781981};
}
910> gcc --version
gcc (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

911> g++ -Wall -o initSelfTest initSelfTest.cc
initSelfTest.cc: In constructor ‘Test2::Test2(int)’:
initSelfTest.cc:23:1: warning: ‘Test2::val_’ is initialized with itself [-Winit-self]
 Test2::Test2 (int val)
 ^~~~~
initSelfTest.cc: In constructor ‘Test::Test(int)’:
initSelfTest.cc:12:13: warning: ‘*<unknown>.Test::val_’ is used uninitialized in this function [-Wuninitialized]
     : val_ {val_}
             ^~~~
initSelfTest.cc: In constructor ‘Test2::Test2(int)’:
initSelfTest.cc:24:13: warning: ‘*<unknown>.Test2::val_’ is used uninitialized in this function [-Wuninitialized]
     : val_ (val_)
             ^~~~
912> ./initSelfTest 
Test::Test; val = 781981; val_ = 0
Test2::Test2; val = 781981; val_ = 0
913> 
1 Answers

You are using the value before it has been initialized, thats undefined behavior, no error message required/guaranteed. In general warnings are not mandated by the standard.

For the "why is it UB not an error?" I can only speculate. Consider that there are cases that look very similar, but are completely fine. For example

struct foo {
  foo& f;
};

foo f{f};

Only dereferencing would cause a problem, but storing the reference for later use is ok. I can imagine that in general it is impossible for compilers to tell such valid cases apart from the evil ones.

Related