在c ++中使用rvalue设置类变量的最有效方法是什么?

时间:2017-06-22 14:01:19

标签: c++ c++11 rvalue-reference rvalue pass-by-rvalue-reference

我刚开始使用c ++ 11 r值。我读了一些教程,但我没有找到答案。

设置类变量的最佳方法(最有效的方法)是什么?低于代码是否正确? (假设std :: string已经定义了移动构造函数和赋值运算符)。

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
}

我知道编译器可以优化我的代码和/或我可以使用重载函数。我只想知道什么是最好的方式。

4 个答案:

答案 0 :(得分:4)

编译器设计师是聪明的人。使用晶莹剔透,因此可维护

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

让编译器担心优化问题。非常好,加糖在上面。

更好的是,不要将伪装成封装的代码。如果您打算提供此类方法,那么为什么不简单地制作str_ public? (除非您打算在成员更改时对您的对象进行其他调整。)

最后,为什么不喜欢std::string的默认构造函数?沟渠str_("")

答案 1 :(得分:4)

rvalue引用的版本通常不会绑定到lvalue(在您的情况下为mytext),您必须移动它,因此构造对象两次,你有一个危险的物体。从rvalue构造时,const lvalue reference应该更慢,因为它会再次执行相同的操作:construct -> move -> move construct

编译器可能会优化开销。

你最好的选择实际上是:

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

这里的编译器令人惊讶地保证推导出参数的类型,并为lvalues调用复制构造函数,为rvalues调用移动构造函数。

<强>更新

Chris Dew指出,构建和移动分配字符串实际上比复制构造更昂贵。我现在确信使用const&参数是更好的选择。 :D

答案 2 :(得分:4)

Herb Sutter's advice就是从标准的C ++ 98方法开始:

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

如果你需要针对rvalues进行优化,请添加一个带右值引用的重载:

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

请注意,std::string的大多数实现使用小字符串优化,因此如果您的字符串很小,则移动与副本相同,您将无法获得任何好处。

使用pass-by-value然后移动(如Adam Hunyadi的回答)是很诱人的,以避免编写多个重载。但赫伯指出,它不会重复使用str_的任何现有容量。如果你用左值多次调用它,它每次都会分配一个新的字符串。如果您有const std::string&重载,那么它可以重用现有容量并避免分配。

如果你真的很聪明,你可以使用一个使用完美转发的模板化设定器,但要使它完全正确,实际上非常复杂。

答案 3 :(得分:3)

您可能使用模板化setString和转发参考:

class StringWrapper
{
private:
    std::string str_;

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