我有一个简单的课程:
class X
{
std::string S;
X (const std::string& s) : S(s) { }
};
我最近读了一些关于rvalues的内容,我一直在想,如果我应该使用rvalue为X
编写构造函数,那么我就能检测到std::string
类型的临时对象?
我认为它应该类似于:
X (std::string&& s) : S(s) { }
据我所知,在支持C ++ 11的编译器中实现std :: string应该在可用时使用它的移动构造函数。
答案 0 :(得分:25)
X (std::string&& s) : S(s) { }
这不是采用 rvalue 的构造函数,而是采用 rvalue-reference 的构造函数。在这种情况下,您不应该使用 rvalue-references 。而是通过值传递,然后移动到成员中:
X (std::string s) : S(std::move(s)) { }
经验法则是,如果您需要复制,请在界面中进行复制。
答案 1 :(得分:21)
为了澄清:传递值的答案没有错。但是,除了一个细节之外,你的第一个猜测是加入string&&
重载:
添加:
X (std::string&& s) : S(std::move(s)) { }
即。您仍然需要move
,因为尽管s
具有声明的对string
的右值引用类型,但表达式 s
用于初始化{{1}是S
类型的左值表达式。
实际上,您首先提出的解决方案(添加了移动)比按值传递解决方案略快。但两者都是正确的。当将lvalue和xvalue参数传递给X的构造函数时,pass-by-value解决方案将字符串的移动构造函数调用一个额外的时间。
在lvalue参数的情况下,无论如何都会创建一个副本,而string的复制构造函数可能比string的移动构造函数要昂贵得多(除了适合短字符串缓冲区的字符串,在这种情况下,移动和复制是大致相同的速度。)
对于xvalue参数(xvalue是已传递给string
的左值),按值传递解决方案需要两个移动构造而不是一个。所以它的价格是rvalue参考解决方案的两倍。但仍然非常快。
这篇文章的重点是要明确:按值传递是一种可接受的解决方案。但它不是唯一可接受的解决方案。使用pass-by-rvalue-ref进行重载更快,但缺点是所需的重载次数随着参数N的增长而缩放为2 ^ N.
答案 2 :(得分:13)
X (std::string s) :S(std::move(s)) {}
现在你可以处理两个l值,它们将被复制到参数然后移入你的类'字符串,和r值,它们将被移动两次(你的编译器希望可以优化这些额外的工作)。 / p>
在大多数情况下(有些例外情况我不会在这里讨论),除了你编写的类的移动构造函数之外,你不应该编写带有r值引用的函数。只要您需要自己的值副本,并且这不仅适用于构造函数,您应该按值获取它,并将其移动到需要的位置。您让类自己的移动构造函数根据它是收到r值还是l值来决定是复制还是移动值。总而言之,引入了r值引用,使我们的生活更轻松,而不是更难。
答案 3 :(得分:5)
由于您需要参数的副本,请按值获取参数。然后,将其移动到您的会员数据中。它是std::string
构造函数,它负责检测给定的参数是左值还是左值,而不是你。
class X
{
std::string s_;
X(std::string s) : s_(std::move(s)) {}
};