我在使用 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 的返回类型设置为引用来摆脱它
-> auto const &
我认为 lambda operator() 和/或复制省略的内联特性会处理“无用”的复制。
编辑:正如 Barry 所解释的,答案是标准禁止函数参数返回的复制省略。
最佳答案
函数参数中没有复制省略(参见 [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 规则删除拷贝,它可以这样做——但在这种情况下不能,因为该拷贝肯定有副作用。
关于c++ - 不设置 lambda 返回类型时在 std::transform 中的附加拷贝,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53657186/