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

additional copy in std::transform when not setting lambda return type

我在使用 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> vy{1};
  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 的 return 类型设置为引用

来摆脱它
-> auto const &

我认为 lambda operator() and/or 复制省略的内联特性会处理 "useless" 复制。

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

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

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call's return object

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