首页 > 解决方案 > 尽管按名称返回,为什么局部变量被复制?

问题描述

在下面的代码片段中,return s给出了警告local variable 's' will be copied despite being returned by name [-Wreturn-std-move]。为什么会这样?

我使用这个 lambda 函数的目标是获取输入字符串的所有权,然后在通过 RVO 修改或移动语义后返回它。我真的很想避免任何复制。

const auto to_upper = [](std::string&& s) {
    std::transform(s.begin(), s.end(), s.begin(), 
        [](unsigned char c){ return std::toupper(c); }
    );
    return s;
};

返回std::move(s)orstd::forward<std::string>(s)将解决问题,但我认为这不是必需的,因为编译器可以省略复制构造函数的使用。另外,我认为我应该使用std::forward,但哪一个是正确的,为什么?

标签: c++c++11

解决方案


在下面的代码片段中,return s给出了警告local variable 's' will be copied despite being returned by name [-Wreturn-std-move]。为什么会这样?

这是因为自动推导出函数的返回值类型或 lambda 捕获调用运算符永远不是引用。见退货类型扣除

如果返回类型不使用decltype(auto),则推演遵循模板实参推演规则。

模板参数推导从不推导引用。

如果需要引用返回值,则必须明确指定:

const auto to_upper = [](std::string&& s) -> std::string&& {
    // ...
    return std::move(s);
}

std::move(s)修复编译器警告,但不会更改返回值类型,除非明确指定引用返回类型。一个例子。编译器警告已损坏。


当函数按值返回时,函数参数不受返回值复制省略的影响。

有关完整详细信息,请参阅复制省略:

return语句中,当操作数是具有自动存储持续时间的非易失性对象的名称时,该对象不是函数参数或 catch 子句参数,并且与函数返回类型。这种复制省略的变体被称为 NRVO,“命名返回值优化”。

class.copy.elision

return具有类返回类型的函数的语句中,当表达式是具有自动存储持续时间的非易失性对象的名称时(除了函数参数或由处理程序的异常声明引入的变量([except. handle]) ) 与函数返回类型相同类型(忽略 cv 限定),可以通过将对象直接构造到函数调用的返回对象中来省略复制/移动操作

函数参数和返回值必须同时共存。构造返回值时,局部变量仍然存在,可以引用函数参数。只有在构造了返回值之后,才会执行局部变量的析构函数。那些析构函数可能会引用函数参数,这就是为什么它的存储不能被重用于之前构造的返回值。


推荐阅读