What's the most efficient way to set a class variable using rvalues ​​in C ++?

I just got started with C ++ 11 r-values. I have read several tutorials but I have not found an answer.

What's the best way (most efficient way) to set a class variable? Is the code below or not? (assuming std :: string has defined a move constructor and an assignment operator).

class StringWrapper
{
private:
    std::string str_;

public:
    StringWrapper() : str_("") {}

    void setString1(std::string&& str) {
      str_ = std::move(str);
    }

    void setString2(const std::string& str) {
      str_ = std::move(str);
    }

    // other possibility?
};

int main() {
    std::string myText("text");

    StringWrapper x1, x2;

    x1.setString?("text"); // I guess here should be setString1
    x2.setString?(myText); // I guess here should be setString2
}

      

I know the compiler can optimize my code and / or use overloading functions. I would only like to know which is the best way.

+3


source to share


4 answers


Herb Sutter's advice to start with the C ++ 98 standard approach:

void setString(const std::string& str) {
  str_ = str;
}

      

And if you need to optimize for rvalues, add an overload that takes an rvalue reference:

void setString(std::string&& str) noexcept {
  str_ = std::move(str);
}

      



Note that most implementations std::string

use some minor string optimizations, so if your strings are small, the move is still the same as the copy, and you won't get any benefit.

It is tempting to use pass-by-value and then move on (as in Adam Hunyadi's answer) to avoid having to write multiple overloads. But Herb indicated that he is not reusing existing capacity str_

. If you call it multiple times with lvalues, it will allocate a new line each time. If you have congestion const std::string&

, it can reuse existing capacity and avoid allocation.

If you're really smart, you can use a templated setter that uses perfect redirects, but getting it completely correct is actually quite tricky.

+4


source


Compiler developers are smart people. Use crystal clear and therefore maintainable

void setString(const std::string& str) {
    str_ = str;
}

      

and let the compiler worry about the optimization. Pretty please, with sugar on top.



Better not masquerade code as encapsulated . If you intend to provide a method like this, then why not just do it str_

public

? (Unless you intend to do other customizations on your object if the member changes.)

Finally, why don't you like the default constructor std::string

? Ditch str_("")

.

+4


source


The referenced version rvalue

does not usually bind to lvalue

(in your case mytext

), you will have to move it and therefore construct the object twice, leaving you with a dangerous object. A const lvalue reference

should be slower in the construction of the rvalue, because he would do the same again construct -> move -> move construct

.

The compiler could optimize the overhead though.

The best would be:

void setString(std::string str) 
{
   str_ = std::move(str);
}

      

Surprisingly, the compiler has to figure out the type of the argument and call the copy constructor for lvalues ​​and the move constructor for rvalues.

Update:

Chris Dew pointed out that building and traversing assigning a string is actually more expensive than copying. Now I am convinced that using an argument const&

is the best option.: D

+4


source


Maybe you can use templatized setString

and forwarding links:

class StringWrapper
{
private:
    std::string str_;

public:
    template<typename T>
    void setString(T&& str) {
        str_ = std::forward<T>(str);
    }
};

      

+3


source







All Articles