Pergunta

I want to know if the compiler is allowed to automatically use the move constructor for wstring in the following setter method (without an explicit call to std::move):

void SetString(std::wstring str)
{
    m_str = str;  // Will str be moved into m_str automatically or is std::move(str) needed?
}

From what I've read it sounds as though the compiler is not allowed to make this decision since str is an lvalue, but it seems pretty obvious that using move here would not change program behavior.

Barring move, will some other sort of copy elision be applied?

Foi útil?

Solução

[is] the compiler [...] allowed to automatically use the move constructor

Yes, it would be nice. But this is not only an optimization, this has real impact on the language.

Consider a move-only type like unique_ptr:

std::unique_ptr<int> f()
{
  std::unique_ptr<int> up;
  return up; // this is ok although unique_ptr is non-copyable.
}

Let's assume your rule would be included into the C++ standard, called the rule of "argument's last occurence".

void SetString(std::unique_ptr<int> data)
{
    m_data = data; // this must be ok because this is "argument's last occurence"
}

Checking if an identifier is used in a return is easy. Checking if it is "argument's last occurence" isn't.

void SetString(std::unique_ptr<int> data)
{
    if (condition) {
      m_data = data; // this is argument's last occurence
    } else {
      data.foo();
      m_data = data; // this is argument's last occurence too
    }
    // many lines of code without access to data
}

This is valid code too. So each compiler would be required to check for "argument's last occurence", wich isn't an easy thing. To do so, he would have to scan the whole function just to decide if the first line is valid. It is also difficult to reason about as a human if you have to scroll 2 pages down to check this.

No, the compiler isn't allowed to in C++11. And he probably won't be allowed in future standards because this feature is very difficult to implement in compilers in general, and it is just a convenience for the user.

Outras dicas

no, move semantics will not be used here, since str can be used in the next code, in fact even if it was rvalue youd still have to std::move force it.. if you want to use move semantics I would advise getting wstring&& str to the function and then using move..

No, the complier is not allowed. Due to some reasons, not only because it is difficult to do. I think copy and move can have side effects and you need to know when you can expect each will be used. For example it is well know that returning a local object will move it - you expect that, it is documented, is OK.

So, we have the following possibilities:

Your example:

void SetString(std::wstring str)
{
     m_str = str;  
}

For r-values: One r-ref in str, plus a copy into m_str. For l-values: a copy in str an a copy in m_str.

We can do it “better” manually:

void SetString( std::wstring str)
{
     m_str = std::move(str);  
}

For r-values: One r-ref in str, plus a move into m_str. For l-values: a copy in str an a move in m_str.

If for some reason (you want it to compile without C++11 without changes but automatically take advantages of C++11 when porting the code?) you don’t want “manually optimize” the code you can do:

void SetString(const std::wstring& str)
{
     m_str = str;  
}

For r-values: One ref in str, plus a copy into m_str. For l-values: a ref in str an a copy in m_str. Never 2 copy.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top