Why the msg
is not being modified after the call to std::move(msg)
?
int main()
{
std::string msg( "Error!" );
std::cout << "before try-catch: " << msg << '\n';
try
{
throw std::invalid_argument( std::move( msg ) );
}
catch ( const std::invalid_argument& ia )
{
std::cerr << ia.what( ) << '\n';
}
std::cout << "after try-catch: " << msg << '\n'; // Here I expect to see an empty msg
// like this: after try-catch:
return 0;
}
I want to move msg
to the ctor of std::invalid_argument
instead of copying it. I thought that msg
should be modified and be left in an unspecified but valid state after the call to std::move
. But this happens:
before try-catch: Error!
Error!
after try-catch: Error!
Why is this happening? Is the move ctor of std::string
not being called? Or is this some kind of aggressive compiler optimization despite using -O0 option?
CodePudding user response:
Here, the only relevant constructor of std::invalid_argument
is:
invalid_argument(const std::string& what_arg);
Const-ref parameter can bind to anything, including an xvalue, which std::move(msg)
is. std::move()
itself is just a cast, the real work of moving data out of a string could have been made inside a constructor. But you can't modify an xvalue taken by a const-ref. The only option you have is to make a copy, leaving msg
unmodified.
Cppreference has the following note, which explains the absence of a constructor taking std::string&&
:
Because copying
std::invalid_argument
is not permitted to throw exceptions, this message is typically stored internally as a separately-allocated reference-counted string. This is also why there is no constructor takingstd::string&&
: it would have to copy the content anyway.