14

我有一个简单的课程:

class X
{
    std::string S;
    X (const std::string& s) : S(s) { }
};

我最近读了一些关于右值的文章,我一直在想,我是否应该为X使用右值编写构造函数,这样我就能够检测到临时std::string类型的对象?

我认为它应该看起来像:

X (std::string&& s) : S(s) { }

据我所知,在支持 C++11 的编译器中实现 std::string 应该在可用时使用它的移动构造函数。

4

4 回答 4

25
X (std::string&& s) : S(s) { }

这不是采用rvalue的构造函数,而是采用rvalue-reference的构造函数。在这种情况下,您不应该使用右值引用。而是按值传递,然后进入成员:

X (std::string s) : S(std::move(s)) { }

经验法则是,如果您需要复制,请在界面中进行。

于 2012-05-31T15:10:41.140 回答
22

为了澄清:按值传递的答案没有错。string&&但是,除了一个细节之外,您对添加重载的第一个猜测也不是:

添加:

X (std::string&& s) : S(std::move(s)) { }

也就是说,您仍然需要 ,move因为尽管s声明了 rvalue 引用的类型string,但用于初始化的表达式 是 type 的左值表达式。sSstring

实际上,您首先提出的解决方案(添加了移动)比传递值解决方案略快。但两者都是正确的。当将左值和 xvalue 参数传递给 X 的构造函数时,按值传递的解决方案会额外调用字符串的移动构造函数。

在左值参数的情况下,无论如何都会进行复制,并且字符串的复制构造函数可能比字符串的移动构造函数昂贵得多(除了适合短字符串缓冲区的字符串,在这种情况下移动和复制大致相同速度)。

对于 xvalue 参数(xvalue 是已传递给 的左值std::move),按值传递解决方案需要两个移动构造而不是一个。因此它的成本是通过右值参考解决方案传递的两倍。但仍然非常快。

这篇文章的重点是要明确:按值传递是一种可接受的解决方案。但这不是唯一可接受的解决方案。使用 pass-by-rvalue-ref 进行重载更快,但缺点是随着参数数量 N 的增加,所需的重载数量会缩放为 2^N。

于 2012-05-31T15:59:31.533 回答
14

不,你不应该。你应该做的是用这样的一个替换你当前的构造函数:

X (std::string s) :S(std::move(s)) {}

现在您可以处理两个 l 值,它将被复制到参数然后移动到您的类的字符串中,以及 r 值,它将被移动两次(您的编译器希望可以优化这个额外的工作)。

在大多数情况下(有一些我不会在这里讨论的例外情况),你不应该编写带有 r 值引用的函数,除了你编写的类的移动构造函数。任何时候您需要自己的值副本,并且这不仅仅适用于构造函数,您应该按值获取它,并将其移动到需要去的地方。您让类自己的移动构造函数根据它收到的是右值还是左值来决定是否应该复制或移动该值。毕竟,引入 r 值参考是为了让我们的生活更轻松,而不是更难。

于 2012-05-31T15:09:36.217 回答
5

由于您需要参数的副本,因此按值获取参数。然后,将其移动到您的会员数据中。std::string负责检测给定参数是右值还是左值的是构造函数,而不是你。

class X
{
    std::string s_;
    X(std::string s) : s_(std::move(s)) {}
};
于 2012-05-31T15:10:00.973 回答