This appears to be a bug in GCC's implementation of copy elision. The C++ standard says the following:
[class.copy.elision] (emphasis mine)
This elision of copy/move operations, called copy elision, is
permitted in the following circumstances (which may be combined to
eliminate multiple copies):
- in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause
parameter) whose scope does not extend beyond the end of the
innermost enclosing try-block (if there is one), the copy/move
operation from the operand to the exception object can be omitted by
constructing the automatic object directly into the exception object
In the following copy-initialization contexts, a move operation might
be used instead of a copy operation:
- if the operand of a throw-expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter)
whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one),
This is a family of optimizations that allows the copy initialization of an exception object to be either avoided or done as efficiently as possible. Now, a common implementation of std::string
move construction is to leave the source string empty. This appears to be exactly what happens to your code. The temp
in the outer scope is moved from (and left empty).
But that is not the intended behavior. The scope of the temp
you throw exceeds (by far) the try block it's thrown in. So GCC has no business applying copy elision to it.
A possible workaround is to place the declaration of temp
inside the while
loop. This initialized a new std::string
object every iteration, so even if GCC
moves from it, it won't be noticeable.
Another workaround was mentioned in the comments and is to make the outer temp
a const object. This will force a copy (since a move operation requires a non-const source object).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…