4

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?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
FrolickingFerret
  • 291
  • 1
  • 2
  • 12

3 Answers3

3

[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.

ipc
  • 8,045
  • 29
  • 33
  • Thanks guys, my curiosity has been satisfied - sounds like using an explicit call to std::move is required here in order to have the compiler perform the optimization. I also found http://stackoverflow.com/questions/6089425/do-compilers-automatically-use-move-semantics-when-a-movable-object-is-used-for shortly after posting which addresses my question a bit more generally. – FrolickingFerret Mar 21 '13 at 21:58
1

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..

Alon
  • 1,776
  • 13
  • 31
  • And what if SetString is inline ?? – qPCR4vir Mar 21 '13 at 18:57
  • 4
    The recommendation is bogus. The function should take by **value** not by *rvalue-reference*. If you take an *rvalue-reference* you need to provide another exactly equal implementation for *lvalue*s – David Rodríguez - dribeas Mar 21 '13 at 19:30
  • Unless this is exactly what you want, to force the usage of this function by rvalue only, and also this gives you the ability to sometimes copy and sometimes not copy if you have the functions overloading, copy-bad – Alon Mar 23 '13 at 14:37
1

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.

qPCR4vir
  • 3,521
  • 1
  • 22
  • 32
  • with pass by value + `std::move` this would be only one move which is way cheaper than one copy. – ipc Mar 21 '13 at 19:16
  • @ipc. Im not sure I understand you. pass by value = copy ? plus the move. Or you mean the function take string && str, and then we have 2 move ? And yes, I agree we can manualy set some move the compiler is not allowed to do self. The best I can see are 2 move if you pass an rvalue (a temporary?) to the function and 2 time move it to have a copy in m_str. I gess, the best we can do is an analog of passing a raw pointer. I know I can be wrong. – qPCR4vir Mar 21 '13 at 19:40
  • 1
    This: `SetString(std::move(wstring("temp")));` would be at most 2 moves if passed by value. – ipc Mar 21 '13 at 20:20
  • @ipc Now I agree with you: 2 move, but not "only one move"...And you need to manualy set move inside the function, with I agree is the most eficient code. But if you dont want (why?), passing a const ref is at last as eficient, and for rvalues better, than passing by value. Or not? – qPCR4vir Mar 21 '13 at 20:44
  • Not. :-) The compiler is allowed to optimize out the copy when passed by value and move the temp directly to the target. – Bo Persson Mar 21 '13 at 21:02
  • @Bo Persson. To what target: str or m_str ? – qPCR4vir Mar 21 '13 at 21:07
  • @qpc - When passing a temporary by value, there is no need to create a copy for the parameter. If you have `m_str = std::move(str);` inside the function, that can be the single move needed. – Bo Persson Mar 21 '13 at 21:13
  • @BoPersson Is "move the temp directly to the target" more eficient than passing a ref? – qPCR4vir Mar 21 '13 at 21:14
  • @qpc - The problem is that if you have a `const` reference you cannot move the value, you have to copy (always). – Bo Persson Mar 21 '13 at 21:16
  • @BoPersson. Ok thank, I understand: we pass an r-ref, and then make a move ("manualy" inside the function). Rigth? – qPCR4vir Mar 21 '13 at 21:18
  • @BoPersson. Could you check if I finally got the true? – qPCR4vir Mar 21 '13 at 21:51
  • @ipc. Could you check if I finally got the true? I edited the answer. – qPCR4vir Mar 21 '13 at 21:52
  • @qPCR4vir: None is "better" than the other, both have trade offs. I generally would either use a single `std::wstring` or the pair `std::wstring&&`/`std::wstring const&`. But `std::wstring` should probably preferred over a single `std::wstring const&` if you later copy it (not always, for example if you use the clone() pattern). – ipc Mar 21 '13 at 22:37