C++ 我应该为std::string使用右值编写构造函数吗?

C++ 我应该为std::string使用右值编写构造函数吗?,c++,string,constructor,c++11,rvalue-reference,C++,String,Constructor,C++11,Rvalue Reference,我有一个简单的课程: class X { std::string S; X (const std::string& s) : S(s) { } }; 最近我读了一些关于rvalue的文章,我一直在想,我是否应该使用rvalue为X编写构造函数,这样我就可以检测std::string类型的临时对象了 我认为应该是这样的: X (std::string&& s) : S(s) { } 据我所知,在支持C++11的编译器中实现std::string应该在可用

我有一个简单的课程:

class X
{
    std::string S;
    X (const std::string& s) : S(s) { }
};
最近我读了一些关于rvalue的文章,我一直在想,我是否应该使用rvalue为
X
编写构造函数,这样我就可以检测
std::string
类型的临时对象了

我认为应该是这样的:

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

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

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

X (std::string s) :S(std::move(s)) {}
现在,您可以处理l值和r值,l值将被复制到参数中,然后移动到类的字符串中,r值将被移动两次(希望您的编译器可以优化这些额外的工作)


在大多数情况下(我将不在这里讨论一些例外情况),您不应该编写接受r值引用的函数,除了您编写的类的move构造函数。任何时候你需要你自己的一个值的副本,这不仅仅适用于构造器,你应该根据值来接受它,并将它移动到需要的地方。您可以让类自己的move构造函数根据接收的是r值还是l值来决定是复制还是移动该值。毕竟,引入r值引用是为了让我们的生活更轻松,而不是更困难。

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

class X
{
    std::string s_;
    X(std::string s) : s_(std::move(s)) {}
};
这不是采用右值的构造函数,而是采用右值引用的构造函数。在这种情况下,不应使用右值引用。而是按值传递,然后移动到成员中:

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

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

为了澄清:传递值答案没有错。但是,除了一个细节之外,您也没有第一次猜测到要添加
字符串&&
重载:

加:

也就是说,您仍然需要
移动
,因为尽管
s
string
有一个声明类型的右值引用,但用于初始化
s
表达式是
string
类型的左值表达式

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

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

对于xvalue参数(xvalue是已传递给
std::move
的左值),按值传递解决方案需要两个move构造,而不是一个。因此,它的价格是通过右值参考溶液的两倍。但是还是很快


这篇文章的目的是要说明:传递值是一个可接受的解决方案。但这并不是唯一可以接受的解决方案。使用pass-by-rvalue-ref重载速度更快,但缺点是所需的重载数量随着参数数量N的增加而增加为2^N。

在这种情况下,为什么不让构造函数接收rvalue引用?@ncasas:combination explosion?@ncasas:那么您需要同时提供
X(std::string&)
X(std::string const&)
(用于构造对象的参数为左值的情况)。这些替代方案很容易由传递值构造函数中的编译器管理,参数将使用现有构造函数,从
std::string(std::string const&)
std::string(std::string&)
中选择来创建副本。也就是说,只提供by-value构造函数可以让编译器使用库的功能,而不是让您重写它。。。。创建接受右值引用的构造函数应限于您确实需要区分参数是右值还是左值的情况,但在这种情况下,您不必在意,您仍然需要复制到成员
std::string
。通过将副本移动到函数的接口,您可以让编译器选择最佳的复制方式,并且通过在内部使用
std::move
,您可以确保不会产生额外的成本。尽管我认为您应该进一步区分这一点。像std::array这样的类型,如果不能有效地移动或者还没有移动构造函数,仍然需要
t const&
方法来避免在接口和实现中有效地复制它两次。我正想知道除了移动构造函数之外,rvalue引用是否还有其他用途(以及标准库提供的
move
forward
功能).你知道其他一些情况下应该使用右值引用吗?@LucTouraille:在模板函数中允许完美的转发。例如,
emplace\u back
make\u shared
哦,对了,当然!我调用了
forward
,甚至没有考虑它的用途…@Dave:因为如果一个函数采用常量l值引用nce收到r值时,您需要将该值复制到成员中,即使您可能只想移动它。我的回答是基于以下一般原则:1)移动通常比复制便宜,有时是非常大的幅度。2)正如霍华德在回答中详细阐述的那样,您希望避免编写多个函数。如果您可以编写多个函数(当您有多个参数时,情况会变得更糟),那么继续编写
X (std::string s) : S(std::move(s)) { }
X (std::string&& s) : S(std::move(s)) { }