【发布时间】:2015-03-04 12:57:09
【问题描述】:
我发现英特尔编译器不会为 std::array 对象生成返回值优化。以下代码恰好位于我的程序的内部循环中,并没有尽其所能进行优化。
std::array<double, 45> f(const std::array<double, 45>& y) {
auto dy_dt = std::array<double, 45>( );
...
return dy_dt;
}
我发现这种行为源于我的标准库实现没有为 std::array 显式定义复制构造函数。下面的代码证明了这一点:
class Test {
public:
Test() = default;
Test(const Test& x);
};
Test f() {
auto x = Test( );
return x;
}
当你用它编译时
icpc -c -std=c++11 -qopt-report=2 test.cpp -o test.o
报告文件显示
INLINE REPORT: (f(Test *)) [1] main.cpp(7,10)
这证明编译器生成了RVO(更改了f的签名,以便可以将新创建的对象放入调用站点的堆栈中)。但是如果你注释掉声明Test(const Test& x);的行,报告文件就会显示
INLINE REPORT: (f()) [1] main.cpp(7,10)
证明没有生成RVO。
在定义 RVO 的 C++11 标准的 12.8.31 中,他们给出的示例有一个复制构造函数。那么,这是英特尔编译器的“错误”还是符合标准的实现?
【问题讨论】:
-
@Cyber 不,如果 RVO 是可能的,它会省略副本。
-
Cyber:不,这里涉及到命名返回值优化。它与移动语义无关。
-
RVO 本身不需要复制构造函数,但要应用 RVO 的代码需要有效的复制或移动构造函数可用。
-
如果你说
Test(const Test& x) = delete;,那么你就没有可用的、有效的复制构造函数了。 -
我所说的几乎是重言式:RVO 无法更改某些代码是否有效,并且要按值返回对象,它必须来自可复制/可移动的类型。因此,无论 RVO 是否发生,您都需要复制/移动构造函数。