std::string 的 move ctor 不能正常工作?

move ctor of std::string does not work properly?

为什么 msg 在调用 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;
}

我想将 msg 移动到 std::invalid_argument 的构造函数而不是复制它。我认为应该修改 msg,并在调用 std::move 后将其保留在未指定但有效的状态。但这种情况发生了:

before try-catch: Error!
Error!
after try-catch: Error!

为什么会这样? std::string 的 move ctor 没有被调用吗?或者这是某种积极的编译器优化,尽管使用了 -O0 选项?

在这里,std::invalid_argument中唯一相关的constructor是:

invalid_argument(const std::string& what_arg);

Const-ref 参数可以绑定到任何东西,包括 xvaluestd::move(msg) 就是。 std::move() 本身只是一个转换,将数据从字符串中移出的真正工作可以在构造函数中完成。但是你不能修改 const-ref 获取的 xvalue。您唯一的选择是复制一份,不修改 msg

Cppreference 具有以下 note,这解释了缺少采用 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 taking std::string&&: it would have to copy the content anyway.