Search code examples
c++rvaluestatic-caststdmove

Why does static_cast in the std::move() wipe value of argument?


My question contains two parts:

  1. Does function static_cast<Т>(arg) alter the innards of arg? Obviously not, according to such code:

    float i1 = 11.5;
    int x = static_cast<int>(i1);
    std::cout << i1<<std::endl;   //11.5
    std::cout << x<<std::endl;    //11
    
  2. Why does such code:

    std::string s1 = "123";
    std::string s2 = std::move(s1);
    std::cout << s1 << std::endl;  //empty
    std::cout << s2 << std::endl;  //123
    

    where std::move() is using only a static_cast to r-value:

    template<typename _Tp>
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
    

    makes s1 an empty string?

I guess, it is because of using the move constructor of string after s2 =. It must wipe the initial string by equating to nullptr or 0 all of the data in the string object. While std::move() by itself is only returning rvalue. Is it correct?

I know my question is a duplicate of something like static_cast to r-value references and std::move change their argument in an initialization, but I have not found a clear explanation.


Solution

  • While reading your question, I had a sense of feeling that you already understood what's happening and yet wanted it to be confirmed.

    I guess, it is because of using move constructor of string after s2 =. It must wipe the initial string by equating to nullptr or 0 all data in string object. While std::move() by itself is only returning rvalue.

    Is it correct?

    Yep, you got it right. That's basically what's happening.

    std::move doesn't move and doesn't change any state of the object "by itself". It just casts a given object's type as a rvalue reference.

    It's a constructor of std::basic_string that eliminates s1 in your example.

    In cppreference, it gives you a brief detail of what it does:

    ...Constructs the string with the contents of other using move semantics. other is left in valid, but unspecified state.

    Try writing a sample code like this to prove your point:

    std::string s = "Hello World";
    (void)std::move(s); // (void) is there to discard the warning message.
    std::cout << s;
    

    You can see s have not changed at all.