为什么以下代码不调用std :: string的移动构造函数?

Bwm*_*mat 2 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;
}
Run Code Online (Sandbox Code Playgroud)

我希望foobar()中的return语句调用move构造函数,因为我返回一个本地(rand()是为了防止NRVO),就像回答std :: move一个局部变量这样的问题的答案

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

Mic*_*urr 5

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

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

在与类返回类型的函数返回语句,当表达是一种非挥发性的自动对象的具有相同的CV-不合格类型作为函数返回类型名称(不是函数或catch子句参数其他),通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

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

后来,标准提到了这一点

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

当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的.如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.[注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数. - 结束说明]

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

你可以强制解决这个问题:

return (rand() % 2) ? std::move(str1) : std::move(str2);
Run Code Online (Sandbox Code Playgroud)