我应该使用rvalues为std :: string编写构造函数吗?

Bar*_*icz 14 c++ string constructor rvalue-reference c++11

我有一个简单的课程:

class X
{
    std::string S;
    X (const std::string& s) : S(s) { }
};
Run Code Online (Sandbox Code Playgroud)

我最近读过一些关于rvalues的内容,我一直在想,如果我应该编写X使用rvalue的构造函数,那么我能够检测出std::string类型的临时对象吗?

我认为应该看起来像:

X (std::string&& s) : S(s) { }
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*eas 25

X (std::string&& s) : S(s) { }
Run Code Online (Sandbox Code Playgroud)

这不是一个采用rvalue的构造函数,而是一个采用rvalue-reference的构造函数.在这种情况下,您不应该使用rvalue-references.而传值,然后移动到所述构件:

X (std::string s) : S(std::move(s)) { }
Run Code Online (Sandbox Code Playgroud)

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

  • @ncasas:那么你需要同时提供一个`X(std :: string &&)`和`X(std :: string const&)`(对于用于构造对象的参数是*lvalue*的情况) .替代方案很容易由*pass-by-value*构造函数中的编译器管理,该参数将使用从`std :: string(std :: string const&)`或`std :: string(std)中选择的现有构造函数. :: string &&)`来创建副本.也就是说,只提供按值构造函数可以让编译器使用库的功能而不是重写它. (6认同)
  • 虽然我认为你应该更多地区分这一点.类似std :: array的类型无法有效移动或者还没有移动构造函数仍然需要`T const&'方法来避免在接口*和*实现中有效地复制它两次. (4认同)
  • 在这种情况下,为什么他不应该有一个构造函数接收rvalue-reference? (3认同)
  • ...创建带*rvalue-references*的构造函数应该仅限于**当*参数是*rvalue*或*lvalue*时需要区分的情况,但在这种情况下你不关心,你需要复制到成员`std :: string`.通过将副本移动到函数的接口,您可以让编译器选择最佳的复制方式,并在内部使用`std :: move`,确保不会产生额外的成本. (3认同)
  • @ncasas:组合爆炸? (2认同)

How*_*ant 21

为了澄清:传值的答案没有错.但是string&&,除了一个细节之外,你的第一个猜测是你没有添加过载:

加:

X (std::string&& s) : S(std::move(s)) { }
Run Code Online (Sandbox Code Playgroud)

即你仍然需要move因为虽然s有一个声明类型的右值引用string,但用于初始化的表达式 是一个类型的左值表达式.sSstring

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

在lvalue参数的情况下,无论如何都会创建一个副本,而string的复制构造函数可能比string的移动构造函数要昂贵得多(除了适合短字符串缓冲区的字符串,在这种情况下,移动和复制大致相同)速度).

在xvalue参数的情况下(xvalue是已传递给的左值std::move),按值传递解决方案需要两个移动构造而不是一个.所以它的价格是rvalue参考解决方案的两倍.但仍然非常快.

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


Ben*_*ley 13

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

X (std::string s) :S(std::move(s)) {}
Run Code Online (Sandbox Code Playgroud)

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

在大多数情况下(有些例外情况我不会在这里讨论),除了你编写的类的移动构造函数之外,你不应该编写带有r值引用的函数.只要您需要自己的值副本,并且这不仅适用于构造函数,您应该按值获取它,并将其移动到需要的位置.您让类自己的移动构造函数根据它是接收r值还是l值来决定是复制还是移动值.毕竟,引入了r值引用,使我们的生活更轻松,而不是更难.


Luc*_*lle 5

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

class X
{
    std::string s_;
    X(std::string s) : s_(std::move(s)) {}
};
Run Code Online (Sandbox Code Playgroud)