在 C++ 中使用右值设置 class 变量的最有效方法是什么?
What is the most efficient way to set class variable using rvalue in c++?
我刚开始使用 c++11 右值。看了一些教程,还是没找到答案
设置 class 变量的最佳方法(最有效的方法)是什么?下面的代码是否正确? (假设 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
}
我知道编译器可以优化我的代码and/or我可以使用重载函数。我只想知道什么是最好的方法。
编译器设计者是聪明人。使用 crystal 清晰,因此可维护
void setString(const std::string& str) {
str_ = str;
}
并让编译器担心优化。拜托了,上面加糖。
更好的是,不要将代码伪装成被封装。如果您打算提供这样的方法,那么为什么不简单地制作 str_
public
? (除非您打算在成员更改时对您的对象进行其他调整。)
最后,为什么不喜欢std::string
的默认构造函数呢?沟 str_("")
.
具有 rvalue
引用的版本通常不会绑定到 lvalue
(在你的情况下,mytext
),你必须移动它,因此构造对象两次,给你留下一个危险的对象。从右值构造时,const lvalue reference
应该更慢,因为它会再次做同样的事情:construct -> move -> move construct
。
虽然编译器可能会优化开销。
你最好的选择实际上是:
void setString(std::string str)
{
str_ = std::move(str);
}
这里的编译器出人意料地保证推断出参数的类型并调用左值的复制构造函数和右值的移动构造函数。
更新:
Chris Dew 指出构造和移动分配字符串实际上比复制构造更昂贵。我现在确信使用 const&
参数是更好的选择。 :D
您可能会使用模板化 setString
和转发引用:
class StringWrapper
{
private:
std::string str_;
public:
template<typename T>
void setString(T&& str) {
str_ = std::forward<T>(str);
}
};
Herb Sutter's advice这个是从标准的C++98入手的做法:
void setString(const std::string& str) {
str_ = str;
}
如果您需要优化右值,请添加一个采用右值引用的重载:
void setString(std::string&& str) noexcept {
str_ = std::move(str);
}
请注意,std::string
的大多数实现都使用小字符串优化,因此如果您的字符串很小,移动与复制一样,您将不会获得任何好处。
很容易使用按值传递然后移动(如 Adam Hunyadi 的回答)以避免必须编写多个重载。但 Herb 指出,它不会重复使用 str_
的任何现有容量。如果你用左值多次调用它,它每次都会分配一个新的字符串。如果您有 const std::string&
过载,那么它可以重新使用现有容量并避免分配。
如果你真的很聪明,你可以使用使用完美转发的模板化 setter,但要使其完全正确实际上是相当复杂的。
我刚开始使用 c++11 右值。看了一些教程,还是没找到答案
设置 class 变量的最佳方法(最有效的方法)是什么?下面的代码是否正确? (假设 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
}
我知道编译器可以优化我的代码and/or我可以使用重载函数。我只想知道什么是最好的方法。
编译器设计者是聪明人。使用 crystal 清晰,因此可维护
void setString(const std::string& str) {
str_ = str;
}
并让编译器担心优化。拜托了,上面加糖。
更好的是,不要将代码伪装成被封装。如果您打算提供这样的方法,那么为什么不简单地制作 str_
public
? (除非您打算在成员更改时对您的对象进行其他调整。)
最后,为什么不喜欢std::string
的默认构造函数呢?沟 str_("")
.
具有 rvalue
引用的版本通常不会绑定到 lvalue
(在你的情况下,mytext
),你必须移动它,因此构造对象两次,给你留下一个危险的对象。从右值构造时,const lvalue reference
应该更慢,因为它会再次做同样的事情:construct -> move -> move construct
。
虽然编译器可能会优化开销。
你最好的选择实际上是:
void setString(std::string str)
{
str_ = std::move(str);
}
这里的编译器出人意料地保证推断出参数的类型并调用左值的复制构造函数和右值的移动构造函数。
更新:
Chris Dew 指出构造和移动分配字符串实际上比复制构造更昂贵。我现在确信使用 const&
参数是更好的选择。 :D
您可能会使用模板化 setString
和转发引用:
class StringWrapper
{
private:
std::string str_;
public:
template<typename T>
void setString(T&& str) {
str_ = std::forward<T>(str);
}
};
Herb Sutter's advice这个是从标准的C++98入手的做法:
void setString(const std::string& str) {
str_ = str;
}
如果您需要优化右值,请添加一个采用右值引用的重载:
void setString(std::string&& str) noexcept {
str_ = std::move(str);
}
请注意,std::string
的大多数实现都使用小字符串优化,因此如果您的字符串很小,移动与复制一样,您将不会获得任何好处。
很容易使用按值传递然后移动(如 Adam Hunyadi 的回答)以避免必须编写多个重载。但 Herb 指出,它不会重复使用 str_
的任何现有容量。如果你用左值多次调用它,它每次都会分配一个新的字符串。如果您有 const std::string&
过载,那么它可以重新使用现有容量并避免分配。
如果你真的很聪明,你可以使用使用完美转发的模板化 setter,但要使其完全正确实际上是相当复杂的。