
On Aug 22, 2011, at 5:55 AM, Sebastian Redl wrote:
On 22.08.2011 13:04, Julian Gonggrijp wrote:
Have we not identified the cases for which an invalid result will be obtained? Can we therefore not maintain -- making use of the semantical definition of move_raw and restricting ourselves to the set of unproblematic types -- that bitwise copying is a safe implementation of move_raw even though in a very strict juridical sense it may be undefined behaviour?
Absolutely not. http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html Undefined behavior isn't just about "a very strict juridical sense" of things. Compilers are allowed to assume that UB doesn't happen. If you memcpy over a non-POD, the compiler is allowed to assume the whole branch containing the memcpy is dead code - it cannot ever be reached, because reaching it would invoke UB. Let's say you have this:
if (x != 42) { memcpy(&nonpod, &source, size); } else { other_code(); } std::cout << x << std::endl;
The behavior of the memcpy is undefined. As such, the compiler can generate any code it wants for this branch - like the code of the second branch, thus eliminating the branching entirely. But not only that. In fact, because the compiler will assume that the program is valid, and entering the memcpy branch would invoke UB, it can deduce that x cannot possibly be anything but 42! That is, the std::cout could output "42" even if you set x to something other than 42, because the optimizer replaced all occurrences of x with the constant 42.
Now, I don't know any compiler that is actually that strict, especially with memcpy, but my point is that *there is no such thing as benign undefined behavior*.
I do (and it's gcc) Consider the following code: void do_something ( int *foo ) { log ( "do_something ( %d )", *foo ); if ( foo == NULL ) { // #1 block of code } else { // #2 block of code } } Under higher optimization levels (-02 and above), gcc will not generate code for the test for NULL (and the associated block #1). It will generate code that looks like this instead: void do_something ( int *foo ) { log ( "do_something ( %d )", *foo ); // #2 block of code } This behavior is enabled by default, and can be disabled with the -fno-delete-null-pointer-checks option. The reasoning behind this (as far as I can tell) is that: * The program indirected the pointer foo on the first line of do_something. * If that pointer is NULL, then the behavior of the program is undefined. ==> Therefore, we can assume that the pointer is != NULL, and omit the test. -- Marshall Marshall Clow Idio Software <mailto:mclow.lists@gmail.com> A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait). -- Yu Suzuki