[Voted into WP at March 2004 meeting.]
Proposed resolution (April 2003): Change 6.10.1 [intro.execution] paragraph 12-13 to read: 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. Conversions applied to the result of
an expression in order to satisfy the requirements of the language
construct in which the expression appears are also considered to be
part of the full-expression.
[Note: certain contexts in C++ cause the evaluation
of a full-expression that results from a syntactic construct other
than expression (7.6.20 [expr.comma]). For example,
in 9.5 [dcl.init] one syntax for
initializer is
[Example:
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
9.5 [dcl.init], 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. ]
struct S {
S(int i): I(i) { }
int& v() { return I; }
private:
int I;
};
S s1(1); // full-expression is call of S::S(int)
S s2 = 2; // full-expression is call of S::S(int)
void f() {
if (S(3).v()) // full-expression includes lvalue-to-rvalue and
// int to bool conversions, performed before
// temporary is deleted at end of full-expression
{ }
}
—end example]
Section 6.8.7 [class.temporary] paragraph 3 says a temp is
destroyed as the last step in
evaluating the full expression. But the expression C().get() has a
reference type. Does 6.8.7 [class.temporary] paragraph 3 require
that the dereference to get a
boolean result occur before the destructor runs, making the code
valid? Or does the code have undefined behavior?
Bill Gibbons:
It has undefined behavior, though clearly this wasn't intended.
The lvalue-to-rvalue conversion that occurs in the "if" statement is
not currently part of the full-expression. From section 6.8.7 [class.temporary] paragraph 3: From section 6.10.1 [intro.execution] paragraph 12: The note in section 6.10.1 [intro.execution] paragraph 12 goes
on to explain that this covers
expressions used as initializers, but it does not discuss
lvalues within temporaries. It is a small point but it is probably worth correcting
6.10.1 [intro.execution] paragraph 12.
Instead of the "implicit call of a function" wording, it might
be better to just say that a full-expression includes any implicit
use of the expression value in the enclosing language construct,
and include a note giving implicit calls and lvalue-to-rvalue
conversions as examples. Offhand the places where this matters include:
initialization (including member initializers),
selection statements,
iteration statements,
return,
throw
class C {
public:
C();
~C();
int& get() { return p; } // reference return
private:
int p;
};
int main ()
{
if ( C().get() ) // OK?
}
Temporary objects are destroyed as the last step in evaluating
the full-expression (6.10.1 [intro.execution])
that (lexically) contains the point
where they were created.
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.