未设置 lambda 返回类型时 std::transform 中的附加副本

Posted

技术标签:

【中文标题】未设置 lambda 返回类型时 std::transform 中的附加副本【英文标题】:additional copy in std::transform when not setting lambda return type 【发布时间】:2018-12-06 17:56:59 【问题描述】:

我在 gcc 7.3 和 c++17 的以下代码示例中苦苦挣扎: https://wandbox.org/permlink/UT3RR9jgRmr3VBWv

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

struct Y 
  Y ( int const & s ) : y(s)  std::cout << "construct y\n"; 
  Y ( Y const & yi ) : y(yi.y)  std::cout << "copy y\n"; 
  Y ( Y && yi ) noexcept : y(yi.y)  std::cout << "move y\n"; 
  int y;
;

struct X 
  X ( Y const & yi ) : x(yi.y)  std::cout << "construct x\n";  
  X ( X const & xi ) : x(xi.x)  std::cout << "copy x\n"; 
  X ( X && xi ) noexcept : x(xi.x)  std::cout << "move x\n"; 
  int x;
;

int main () 
  std::vector<Y> vy1;
  std::vector<X> vx;
  vx.reserve(vy.size());
  std::cout << "begin transform\n";
  std::transform(begin(vy), end(vy), std::back_inserter(vx), [] (auto const & y)  return y; );

输出是

construct Y  
copy Y  
begin transform  
copy Y  
construct X  
move X

为什么 Y 的第二个副本(在转换中)会发生?我可以通过将一元 lambda 的返回类型设置为引用来摆脱它

-> auto const &

我认为 lambda operator() 的内联性质和/或复制省略会处理“无用”的副本。

编辑:正如 Barry 解释的那样,答案是标准禁止函数参数返回的复制省略。

【问题讨论】:

可能重复:***.com/questions/28659879/… 为什么要在这里应用复制省略? 提示:[] (Y const &amp; y) return y; 的返回类型是Y,而不是Y const &amp; copy elision visible side effect的可能重复 为什么这可能是重复的?由于 Barry 解释的原因,我没有复制省略。这对我来说似乎与您链接到的问题不同 【参考方案1】:

函数参数没有复制省略(见[class.copy.elision]/1.1,重点是我的):

这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):

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

lambda 是微不足道的和内联的这一事实并不重要 - 该副本不是省略的候选者。当然,如果编译器可以确定它可以根据 as-if 规则删除副本,它可以这样做 - 但在这种情况下它不能,因为该副本肯定有副作用。

【讨论】:

以上是关于未设置 lambda 返回类型时 std::transform 中的附加副本的主要内容,如果未能解决你的问题,请参考以下文章

使用“事件”调用类型时未调用 AWS Lambda

no suitable ctr exists to convert from 'int' to 'std::basic_string<char,std::char_tra

为啥在编译时不检查 lambda 返回类型?

c# blazor 避免在委托类型返回 'void' 时使用 'async' lambda

Lambda 异步调用

为啥 datadog lambda 检测返回“datadog-lambda-js/handler.handler 未定义或未导出”?