【问题标题】:Why does the following code not invoke std::string's move constructor?为什么以下代码不调用 std::string 的移动构造函数?
【发布时间】:2014-10-07 18:03:08
【问题描述】:

以下代码,在 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 语句调用移动构造函数,因为我正在返回一个本地(rand() 是为了防止 NRVO),就像对Returning std::move of a local variable等问题的回答所说的那样

上下文是我试图在此处为我的另一个问题添加另一个示例:https://softwareengineering.stackexchange.com/questions/258238/move-semantics-in-c-move-return-of-local-variables

【问题讨论】:

  • 如果删除foo变量定义,它是否使用移动构造函数?我猜编译器足够聪明,可以知道您已经引用了使移动无效的字符串(再次猜测)。
  • 不,如果我这样做,它仍然使用复制构造函数。
  • 返回的“先尝试右值”仅适用于“如果满足或将满足复制省略标准,除非源对象是函数参数这一事实”。在 C++14 中,它扩展到 return 语句直接命名本地对象的所有情况。 (有关标准的完整引用,请参阅this answer。)您的条件表达式两者都不是。
  • 你说你返回的是本地人。你不。 (rand() % 2) ? str1 : str2 不是本地人。
  • @T.C. - 就是这样,将其更改为 if/else 而不是三元运算符使其使用移动构造函数。我似乎有点懒惰(尽管从语义上看它似乎不应该有所作为)

标签: c++ c++11


【解决方案1】:

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

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

在具有类返回类型的函数的返回语句中,当 表达式是非易失性自动对象的名称(除了 具有相同 cv-unqualified 的函数或 catch 子句参数) type 作为函数返回类型,可以进行复制/移动操作 通过将自动对象直接构造到 函数的返回值

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

后来,标准提到了

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

当满足或将满足省略复制操作的条件时 遇到源对象是函数参数这一事实, 并且要复制的对象由左值指定,重载 首先执行为副本选择构造函数的分辨率 好像对象是由右值指定的。如果重载决议 失败,或者如果所选的第一个参数的类型 构造函数不是对对象类型的右值引用(可能 cv 限定),再次执行重载决议,考虑到 对象作为左值。 [注:此两阶段过载解决方案必须 无论是否会发生复制省略,都将执行。它 如果不执行省略,则确定要调用的构造函数, 并且选择的构造函数必须是可访问的,即使调用是 省略。 ——尾注]

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

您可以强制执行该问题:

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

【讨论】:

  • 它可以与if (rand() &amp; 2) return str1; else return str2;一起使用吗?
猜你喜欢
  • 2023-03-16
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多