4

From a comment on this answer:

Class members are initialized in their order of declaration. By this logic, the following constructor should invoke undefined behaviour:

struct Foo
{
   Bar a;
   Bar b;

   Foo(Bar c) : a(b = c) { }
};

Patently, we are assigning to b first before a has been initialized. Assigning to an uninitialized object should be UB. It's not surprising that the code "works" with Bar = int, but if I make Bar a heavy class with constructors, I see that b does indeed get initialized before a.

(For extra lunacy, we can even say Foo(Bar c, Bar d) : a(b = c), b(d) { }, still with no warning.)

Yet GCC 4.6.1 doesn't warn about this. Is this acceptable, well-defined behaviour, or is this strictly wrong?

Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    I believe that the reason for UB is the hardness of detection of such situations. You can dereference NULL's as well, you can't expect a compiler to give you warnings and errors on any mistake that you make with your code. Sometimes a common sense is needed. – littleadv Nov 04 '11 at 00:35
  • 1
    I don't believe it's defined. Assume `Bar` has a non-trivial constructor; clearly, `b.operator=()` is being called before `b` is constructed. But I do not know for certain. – ephemient Nov 04 '11 at 00:49
  • Is `b` getting initialized before `a`, or is it simply being assigned to before `a`? – Dennis Zickefoose Nov 04 '11 at 05:09
  • I believe if you're trying to test compiler too much that's even for some undefined expressions, you're at problem. Not every compiler perfect about giving warning or same implementation across – sarat Nov 04 '11 at 06:11

1 Answers1

4

Given a Bar where non-initialized state actually matters to the assignment operator, I get warnings from GCC:

#include <iostream>
struct Bar {
    int n;
    Bar(int v) : n(v) {
            std::cout << "Bar " << n << " constructed\n";
    }
    Bar& operator=(const Bar& other) {
        std::cout << "Bar " << n << " assigned from " << other.n << "\n";
        n = other.n;
        return *this;
    }
};
struct Foo
{
   Bar a;
   Bar b;

   Foo(Bar c, Bar d) : a(b = c), b(d) { }
};

int main()
{
        Foo f(Bar(1), Bar(2));
}

test: https://ideone.com/VDZzG

test.cc: In function ‘int main()’:
test.cc:8:32: warning: ‘*((void*)(& f)+4).Bar::n’ is used uninitialized in this function [-Wuninitialized]
test.cc:23:13: note: ‘*((void*)(& f)+4).Bar::n’ was declared here

Other compilers I've tried don't seem to care, though...

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • Visual C++ 9.0 (2008) gives following output Bar 2 constructed Bar 1 constructed Bar -858993460 assigned from 1 – sarat Nov 04 '11 at 06:10