在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
答案 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);