为什么以下代码不调用std :: string' s构造函数?

时间:2014-10-07 18:03:09

标签: c++ c++11

在VS2013上编译的以下代码从不调用std :: string的移动构造函数(通过设置断点检查,而是调用const ref复制构造函数。

#include <iostream>
#include <string>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */

struct foo
{
    foo(std::string& str1, std::string& str2) : _str1(str1), _str2(str2) {}

    ~foo() { std::cout << "Either \"" << _str1 << "\" or \"" << _str2 << "\" was returned." << std::endl; }

    std::string& _str1;
    std::string& _str2;
};

std::string foobar()
{
    std::string str1("Hello, World!");
    std::string str2("Goodbye, cruel World.");
    foo f(str1, str2);

    srand(time(NULL));

    return (rand() % 2) ? str1 : str2;
}

int main()
{
    std::cout << "\"" << foobar() << "\" was actually returned." << std::endl;

    return EXIT_SUCCESS;
}

我希望foobar()中的return语句调用move构造函数,因为我返回一个本地(rand()是为了防止NRVO),就像Returning std::move of a local variable <等问题的答案所说的那样/ p>

这样做的背景是我试图在这里为我的另一个问题添加另一个例子:https://softwareengineering.stackexchange.com/questions/258238/move-semantics-in-c-move-return-of-local-variables

1 个答案:

答案 0 :(得分:5)

C ++ 11有一个特殊情况,当它是一个局部变量时允许复制/移动椭圆,并用作函数的返回表达式:

C ++ 11 12.8 / 31“复制和移动类对象”:

  

在一个带有类返回类型的函数的return语句中   expression是非易失性自动对象的名称(除了   一个函数或catch子句参数)具有相同的cv-unqualified   键入函数返回类型,复制/移动操作即可   通过直接构造自动对象省略   函数的返回值

但是这种复制省略的情况不符合,因为你拥有的return语句不仅仅是“非易失性自动对象的名称”。

后来,标准提到了

C ++ 11 12.8 / 32“复制和移动类对象”:

  

当符合或将要执行复制操作的标准时   因为源对象是一个函数参数这个事实,   并且要复制的对象由左值,超载指定   首先执行选择复制的构造函数的分辨率   好像对象是由右值指定的。如果超载分辨率   失败,或者如果所选的第一个参数的类型   构造函数不是对象类型的右值引用(可能   cv-qualified),重新执行重载决策,考虑到   对象作为左值。 [注意:这个两级重载决议必须   无论是否会发生复制,都会执行。它   如果没有执行elision,则确定要调用的构造函数,   并且即使呼叫是必须的,也必须可以访问所选的构造函数   省略。 - 结束说明]

这允许即使返回指定左值也可以使用移动操作。但是,此特殊情况仅适用于第一句的条件,在您的示例return语句的情况下不符合这些条件。

您可以强制解决问题:

return (rand() % 2) ? std::move(str1) : std::move(str2);