4

I have a Text class that contain a std::string. A method SetText as follow :

void Text::SetText( const std::string& str )
{
    m_str = str;
}

Since this method will almost always been called with rvalues as parameter, I thought about move constructors. I understand the basics, but that's all. So I made tests and came to the conclusion that another function like that would be better, and once the move constructor and move assignement were defined, there could be performance gains :

void Text::SetText( std::string&& str )
{
    m_str = move( str );
}

There are my questions :

  • Does it works with std container? Does they provide move constructor and assignements?
  • Is move semantics usefull when there is not heap allocations is the class ? ( I mean no heap allocation at all, so no smart pointers as class member )

Thanks.

Aulaulz
  • 459
  • 4
  • 15

1 Answers1

7

Your code is correct. But the preferred C++11 approach is:

void Text::SetText(std::string str)
{
    m_str = move(str);
}

That is, pass by value and move from it.

This will do to moves if you pass an rvalue and one copy and one move if you pass an lvalue where in the later case the one copy is necessary.

Does it works with std container? Does they provide move constructor and assignements?

Yes, standard library provides move constructors and move assignments for all classes where it can be made more efficient than copy.

Is move semantics usefull when there is not heap allocations is the class ? ( I mean no heap allocation at all, so no smart pointers as class member )

Except a few cases with other kinds of resources, no.

If all data of the object is stored directly in the object, they have to be copied and there is no other work that could be saved by moving.

Also note that if you make the function inline, compiler should be able to elide one of the moves in the rvalue case. It won't be able to elide it in the lvalue case unless the operator= is also inlined, because it won't be allowed to replace the moving assignment with copying assignment.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • 1
    Any particular reason you don't [`std::swap(m_str, str)`](http://stackoverflow.com/a/3109981/1322972)? – WhozCraig Jul 31 '14 at 08:15
  • To provide a `void Text::SetText( std::string&& str )` overload is also correct. – TNA Jul 31 '14 at 08:26
  • 1
    @TNA: Yes, but you need _both_ `std::string &&` _and_ `std::string const &` overloads and don't get much benefit back. – Jan Hudec Jul 31 '14 at 08:53
  • Using void Text::SetText(std::string str), it will make a copy in case it is an lvalue right? Isn't it better to avoid the copy? – Aulaulz Jul 31 '14 at 08:58
  • 1
    @Aulaulz: If you pass an lvalue, there will be one copy that is not avoidable. You can do it in the argument (with pass-by-value, which needs additional move) or in the assignment (with pass-by-reference, which needs two overloads), but you can't avoid it. – Jan Hudec Jul 31 '14 at 09:15
  • Modern compilers completely ignore the `inline` keyword for the purposes of inlining. – fredoverflow Jul 31 '14 at 09:18
  • @WhozCraig: `std::swap` is used when implementing `operator=` of an object. But if implementing a setter, `std::swap` is no better, and in rare cases may be worse, than move assignment. – Jan Hudec Jul 31 '14 at 09:19
  • Can you present an example of such a case where it may be *worse* ? Or at least a link to follow? – WhozCraig Jul 31 '14 at 09:21
  • @FredOverflow: Modern compilers decide by themselves what they should inline, but inlining functions that are compiled into different unit may not always be possible. So _defining_ functions inline _in the header_ makes sense in that it ensures the compiler can inline if it so chooses. – Jan Hudec Jul 31 '14 at 09:21