5

If I have this code:

class A { ... };
class B { ... };

void dummy()
{
    A a(...);
    B b(...);
    ...
}

I know that variables a and b will be destroyed in reverse allocation order (b will be destroyed first, then a); but can I be sure that the optimizer will never swap the allocation and construction of a and b? Or I must use volatile to enforce it?

Loghorn
  • 2,729
  • 17
  • 22
  • 6
    I am pretty sure that the order is guaranteed by the standard. – Björn Pollex Mar 10 '11 at 09:38
  • It's hard to imagine how swapping the instantiation order would result in more optimal code. Obviously if `b` depends on `a` somehow, it could not do it—but why would it? – wallyk Mar 10 '11 at 09:42
  • 3
    Fairly easy to imagine. In fact, it's quite possible to imagine the instantiations to overlap. The compiler generates two sequences of assembly instructions, and the optimizer reorders them. it's quite possible that the last instruction of the first sequence is swapped with the first argument of the second sequence, when that last instruction depends on the previous instruction. Optimizers have a good understanding of such value dependencies. – MSalters Mar 10 '11 at 12:48

4 Answers4

6

The only guarantees are that any observable side effects (that is, reads and writes to volatile objects and calls to I/O functions) of the construction of a will happen before any observable side effects of the construction of b, and any side effects of a required by b will happen before they are needed.

It's hard to imagine why you would need a stricter ordering than that, but making the objects volatile will ensure that a is completely initialised before initialising any part of b, although some code from the constructor could still happen before a is complete.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • I need the strict ordering because A locks a mutex on construction and releases it on destruction and I need it to be locked while constructing B – Loghorn Mar 10 '11 at 09:59
  • 5
    @AlessandroV: Locking the mutex will impose a memory barrier, giving you the ordering you need. This is outside the scope of the current C++ standard, but any library that implements a usable mutex will ensure that this is the case. – Mike Seymour Mar 10 '11 at 10:04
  • 1
    (Note that, by "the current C++ standard", I was referring to C++03. C++11 does require that locking a mutex imposes an appropriate barrier.) – Mike Seymour Feb 04 '13 at 14:21
3

The only thing you can be sure of is that the construction and allocation of a will be before b. As long as you separate your statements with ;, they'll be executed in order, regardless of optimization.

volatile will not change that, what it does is preventing the compiler from caching the values between the accesses.

littleadv
  • 20,100
  • 2
  • 36
  • 50
0

Ok, I find such statements in standard but I am a little confused by other's answer.

Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function. (In other words, function executions do not interleave with each other.)

It only ensures function calling do not interleave with each other, but the functions are indeterminately sequenced, meaning that,

Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which.

user534498
  • 3,926
  • 5
  • 27
  • 52
  • 1
    The quotes refer to the situation when you have several function calls within the same full-expression, such as int i = f() + g();. The key here is *not otherwise specifically sequenced*. A semicolon specifically separates the two expressions, the former being sequenced before the latter – decltype Mar 10 '11 at 09:53
0

I faced with the problem. An optimizer reordered a code. To prevent reordering I would recommend two ways:

1) Incapsulate your objects inside a structrure:

class A { ... };
class B { ... };

struct C {
     A a;
     B b;
};

void dummy()
{
    C c;
}

2) Put variables inside block operators:

class A { ... };
class B { ... };

void dummy()
{
    A a;
    {
        B b;
    }
}
ixjxk
  • 121
  • 1
  • 4