我刚开始使用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
}
我知道编译器可以优化我的代码和/或我可以使用重载函数。我只想知道什么是最好的方式。
答案 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);
}
};