其实，C++ 标准对于具体编译器实现的限制还是比较宽松的。C++ 标准在其语义逻辑基础上定义一个“抽象机器”，这个抽象机器保证对 C++ 程序的语义逻辑作出相应的操作。C++ 编译器实现只要在其实现环境上保证其产生的执行程序的行为“看起来好像”与这个抽象机器的行为一致就可以了。这一节指出了实现与虚拟机器的关系，怎么判定一个实现和标准是否符合，副作用和顺序点的概念。
1.9 Program execution [intro.execution]
The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.i)
i) This provision is sometimes called the “as-if” rule, because an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program. For instance, an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.
Certain aspects and operations of the abstract machine are described in this International Standard as implementation-defined (for example,
sizeof(int)). These constitute the parameters of the abstract machine. Each implementation shall include documentation describing its characteristics and behavior in these respects. Such documentation shall define the instance of the abstract machine that corresponds to that implementation (referred to as the “corresponding instance" below).
Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine. An instance of the abstract machine can thus have more than one possible execution sequence for a given program and a given input.
Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer). [Note: this International Standard imposes no requirements on the behavior of programs that contain undefined behavior. ]
A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible execution sequences of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution sequence contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
The observable behavior of the abstract machine is its sequence of reads and writes to
volatile data and calls to library I/O functions.ii)
volatile 数据的读写和对库 I/O 函数的调用的序列。ii)
ii) An implementation can offer additional library I/O functions as an extension. Implementation that do so should treat calls to those functions as “observable behavior” as well.
实现可以提供额外的 I/O 库函数作为扩展，实现应该把对这些函数的调用同样看作“可观察行为”。
Accessing an object designated by a
volatile lvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression might produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.iii)
volatile 左值（3.10）的对象，更改一个对象，调用 I/O 库函数，或调用一个作任何这类操作的函数的操作都具有副作用，副作用改变了执行环境的状态。表达式的求值可能带来副作用。在被称为顺序点的某些执行序列的特定点，所有之前的求值带来的副作用都必须已经完成，所有之后的执行序列的副作用都还没发生。iii)
iii) Note that some aspects of sequencing in the abstract machine are unspecified; the preceding restriction upon side effects applies to that particular execution sequence in which the actual code is generated. Also note that when a call to a library I/O function returns, the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) may not have completed yet.
注意，某些情况下抽象机器执行的次序是未指定的；上述有关副作用的限制适用于那些生成实际代码的执行序列。还要注意，当从 I/O 库函数调用返回时，即使这个函数调用执行的某些外部动作（比如硬件 I/O 动作本身）还没完成，这个函数调用的副作用仍然被认为是完成的。
Once the execution of a function begins, no expressions from the calling function are evaluated until execution of the called fucntion has completed.iv)
iv) In other words, function executions do not interleave with each other.
When the processing of the abstract machine is interrupted by receipt of a signal, the value of objects with type other than
volatile sig_atomic_t are unspecified, and the value of any object not of
volatile sig_atomtic_t that is modified by the handler becomes undefined.
volatile sig_atomic_t 以外的任何类型的对象都是未指定的。信号处理器更改的除
volatile sig_atomic_t 外任何类型的对象的值都将变为未指定的。
An instance of each object with automatic storage duration (3.7.2) is associated with each entry into its block. Such an object exists and retains its last-stored value during the execution of the block and while the block is suspended (by a call of a function or receipt of a signal).
The least requirements on a conforming implementation are:
- At sequence points, volatile objects are stable in the sense that previous evaluations are complete and subsequent evaluations have not yet occurred.
在顺序点，volatile 对象之前的求值应该已经完成，其之后的求值应该还未发生，即此 volatile 对象应该是稳定的。
- At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced.
- The input and output dynamics of interactive devices shall take place in such a fashion that prompting messages actually appear prior to a program waiting for input. What constitutes an interactive device is implementation-defined.
[Note: more stringent correspondences between abstract and actual semantics may be defined by each implementation. ]
A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.
[Note: certain contexts in C++ cause the evaluation of a full-expression that results from a syntactic construct other than expression (5.18). For example, in 8.5 one syntax for initializer is
( expression-list )
but the resulting construct is a function call upon a constructor function with expression-list as an argument list; such a function call is a full-expression. For example, in 8.5, another syntax for initializer is
but again the resulting construct might be a function call upon a constructor function with one assignment-expression as an argument; again, the function call is a full-expression. ]
[Note: the evaluation of a full-expression can include the evaluation of subexpressions that are not lexically part of the full-expression. For example, subexpressions involved in evaluating default argument expressions (8.3.6) are considered to be created in the expression that calls the function, not the expression that defines the default argument. ]
[Note: operators can be regrouped according to the usual mathematical rules only where the operators really are associative or commutative.v) For example, in the following fragment
int a, b;
a = a + 32760 + b + 5;
the expression statement behaves exactly the same as
a = (((a + 32760) + b) + 5);
due to the associativity and precedence of these operators. Thus, the result of the sum
(a + 32760) is next added to
b, and that result is then added to 5 which results in the value assigned to
a. On a machine in which overflows produce an exception and in which the range of values representable by an
int is [-32768,+32767], the implementation cannot rewrite this expression as
a = ((a + b) + 32765);
since if the values for
b were, respectively, -32754 and -15, the sum
a + b would produce an exception while the original expression would not; nor can the expression be rewritten either as
a = ((a + 32765) + b);
a = (a + (b + 32765));
since the values for
b might have been, respectively, 4 and -8 or -17 and 12. However on a machine in which overflows do not produce an exception and in which the results of overflows are reversible, the above expression statement can be rewritten by the implementation in any of the above ways because the same result will occur. ]
v) Overloaded operators are never assumed to be associative or commutative.
There is a sequence point at the completion of evaluation of each full-expressionvi).
vi) As specified in 12.2, after the “end-of-full-expression” sequence point, a sequence of zero or more invocations of destructor functions for temporary objects takes place, usually in reverse order of the construction of each temporary object.
When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the functionvii). Several contexts in C++ cause evaluation of a function call, even though no corresponding function call syntax appears in the translation unit. [Example: evaluation of a
new expression invocation of a conversion function (12.3.2) can arise in contexts in which no function call syntax appears. ] The sequence points at function-entry and function-exit (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.
vii) The sequence point at the function return is not explicitly specified in ISO C, and can be considered redundant with sequence points at full-expressions, but the extra clarity is important in C++. In C++, there are more ways in which a called function can terminate its execution, such as the throw of an expression.
In the evaluation of each of the expressions
a && b
a || b
a ? b : c
a , b
using the built-in meaning of the operators in these expressions (5.14, 5.15, 5.16, 5.18), there is a sequence point after the evaluation of the first expressionviii).
viii) The operators indicated in this paragraph are the built-in operators, as described in clause 5. When one of these operators is overloaded (clause 13) in a valid context, thus designating a user-defined operator function, the expression designates a function invocation, and the operands form an argument list, without an implied sequence point between them.