12

I was refreshing my understanding of value-initialisation versus default-initialisation, and came across this:

struct C {
    int x;
    int y;
    C () { }
};

int main () {
    C c = C ();
}

Apparently this is UB because

In the case of C(), there is a constructor that is capable of initializing the x and y members, so no initialization takes place. Attempting to copy C() to c therefore results in undefined behavior.

I think I understand why, but I'm not certain. Can someone please elaborate?

Does that mean this is also UB?

int x; x = x;

Incidentally, with regards to value initialisation, is the following guaranteed to be zero?

int x = int ();
spraff
  • 29,265
  • 19
  • 105
  • 197
  • Not sure what you mean. If you mean values of x and y will be uninitialized then yes, but you have answered your own question then (as constructor isn't doing it). As for C c=C(); I think that's totally valid. – Sid Feb 06 '12 at 16:42
  • I thought it was totally valid too, until someone claimed otherwise. As I read it, the first snippet can only by UB if the second one also is, otherwise it's a simple uninitialised value, without nasal deamons. – spraff Feb 06 '12 at 16:45
  • 2
    @Sid: No, using the value of an uninitialised object gives undefined behaviour. – Mike Seymour Feb 06 '12 at 16:54
  • 1
    @MikeSeymour I think the OP understands that but wants to know whether the statement C c = C(); is by itself UB. It's syntactically and semantically correct. Sure x and y will be uninitialized as the constructor isn't doing anything - so it's not good programming practice of course. – Sid Feb 06 '12 at 16:56
  • 1
    @Sid: I was disagreeing with your statement that `C c = C();` is valid. The uninitialised values of the members of `C()` are used to initialise `c`, and using those values gives undefined behaviour; therefore it is not valid. – Mike Seymour Feb 06 '12 at 16:59
  • 1
    @Sid: then the crucial observation is just that if copy elision does not take place then `C c = C();` "uses" the uninitialized values of the members `x` and `y` of the temporary created by `C()`. So it is UB. – Steve Jessop Feb 06 '12 at 17:00
  • Would the downvoters like to explain what has upset them? – spraff Feb 06 '12 at 17:01
  • @SteveJessop Ok I see what you and Mike Seymour are saying now. Agreed it is UB. – Sid Feb 06 '12 at 17:01

3 Answers3

13

Your first example has undefined behavior because the default, compiler generated copy constructor will do a memberwise copy, ints may have trapping values, and reading a trapping value to copy it may cause the program to crash.

In practice, I can't imagine this ever actually crashing; the compiler will almost certainly optimize the copy out, and even if it didn't, it would likely use some special bitwise copy which would copy without checking for trapping values. (In C++, you are guaranteed to be able to copy bytes.)

For the second case, again, undefined behavior. Although in this case, you have assignment rather than copy construction, and the compiler is less likely to optimize it away. (There is no assignment in your first example, only copy construction.)

For the third, yes. An initializer with an empty parenthese (and no user defined default initializer to override it) first performs zero initialization (exactly as occurs for variables with static lifetime).

James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • +1, some debugging tools run the application in a virtual machine that does trap (in some cases) identifying this as a potential problem. But I agree that in a real case scenario chances are that you will just copy uninitialized memory and move it around. – David Rodríguez - dribeas Feb 06 '12 at 17:00
  • In C++14 the references is [dcl.init]/12 "] If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases:" - and no case covers this code – M.M Apr 18 '16 at 22:32
1

I don't think this is actually undefined behavior although the values in c have unspecified values. That is, the behavior of the program is well defined as long as you don't end up using these unspecified values. If you use them, e.g. in a condition or to print them, the results are not defined. However, I don't think the program is allowed to do anything weird.

With respect to using the default constructor on built-in types, this is guaranteed to yield the type's zero value, i.e. 0 for integers, 0.0 for floating point types, etc. This also extends to the members of types without a constructor. Once there is any constructor you need to take care of constructing your members without a constructor yourself.

Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356
  • 1
    Ooh, you vs. James Kanze in a straight gunfight! He says that the copy constructor "uses" the uninitialized values, and that doing so is UB. You say otherwise. – Steve Jessop Feb 06 '12 at 17:18
  • @SteveJessop but this answer seems more correct, isn't it? :) , +1 – Mr.Anubis Aug 22 '12 at 14:43
  • @Mr.Anubis: there seems to be disagreement at the highest levels what you can do in C++ with an uninitialized value. There's some text in the standard that says you cannot perform an lvalue-rvalue conversion, but one would think that (in common with other object representations) you can access it as `unsigned char`. In practice, it's unlikely that the generated copy ctor does anything other than copy the data blindly, but to play safe I believe that James is correct about what the standard permits. e.g copying could incidentally check parity bits, if `int` has any, with UB if they're wrong. – Steve Jessop Aug 22 '12 at 16:18
  • @SteveJessop aah, got it. Thanks – Mr.Anubis Aug 22 '12 at 16:43
-1

No. What will happen is most compilers will optimize the setting of the variable out, but those that do not will simply not change the value of x. It is the same as the following code:

int x = 0;
x = 0;

It's not that the second line won't execute, it just won't do anything.

Richard J. Ross III
  • 53,315
  • 24
  • 127
  • 192
  • The point is that if my posted snippets are UB then compiler optimisations might cause *any* result. – spraff Feb 06 '12 at 16:46
  • 3
    I disagree, technically reading from an unset value is undefined behavior. Whether the compiler optimizes the read away is *one* of the possible outcome of undefined behavior. – Matthieu M. Feb 06 '12 at 16:49