And*_*der 7 c++ rvalue compiler-optimization nrvo
考虑以下代码:
std::vector<int> Foo() {
std::vector<int> v = Bar();
return v;
}
Run Code Online (Sandbox Code Playgroud)
return v是O(1),因为NRVO将省略副本,v 直接在存储中构造,否则将移动或复制函数的返回值.现在考虑功能类似的代码:
void Foo(std::vector<int> * to_be_filled) {
std::vector<int> v = Bar();
*to_be_filled = v;
}
Run Code Online (Sandbox Code Playgroud)
这里可以做出类似的论证,因为*to_be_filled = v它可以被编译成O(1)move-assign,因为它是一个超出范围的局部变量(编译器应该很容易验证v没有外部引用)这种情况,因此在最后一次使用时将其推广到rvalue).是这样的吗?有没有微妙的理由呢?
此外,感觉这种模式可以扩展到左值超出范围的任何上下文:
void Foo(std::vector<int> * to_be_filled) {
if (Baz()) {
std::vector<int> v = Bar();
*to_be_filled = v;
}
...
}
Run Code Online (Sandbox Code Playgroud)
do/can /是否有用/合理期望编译器找到诸如之类的模式*to_be_filled = v然后自动优化它们以假设rvalue语义?
编辑:
g ++ 7.3.0 在-O3模式下不执行任何此类优化.
Nic*_*las 10
不允许编译器任意决定将左值名称转换为要移动的左值.它只能在C++标准允许的情况下这样做.如在return声明中(仅在其声明时return <identifier>;).
*to_be_filled = v;将始终执行副本.即使它是可以访问的最后一个语句v,它始终是一个副本.编译器不允许更改它.
我的理解是返回v是O(1),因为NRVO(实际上)将v变成rvalue,然后使用std :: vector的move-constructor.
这不是它的工作原理.NRVO将彻底消除移动/复制.但是return <identifier>;成为右值的能力不是"优化".这实际上是编译器将它们视为rvalues 的要求.
编译器可以选择复制省略.编译器没有选择什么return <identifier>;.所以上面要么根本不移动(如果发生NRVO),要么移动对象.
有没有微妙的理由呢?
这是不允许的一个原因是因为语句的位置不应随意改变该语句的作用.看,return <identifier>;将始终从标识符移动(如果它是一个局部变量).它在函数中的位置并不重要.由于是一个return陈述,我们知道如果return执行,那么它将不会被执行.
对于任意陈述,情况并非如此.表达式的行为*to_be_filled = v;不应该根据它在代码中的位置而改变.您不应该仅仅因为向该函数添加另一行而将移动转换为副本.
另一个原因是任意语句很快就会变得非常复杂.return <identifier>;很简单; 它将标识符复制/移动到返回值并返回.
相比之下,如果你有一个引用会发生什么v,并以to_be_filled某种方式被使用.当然,在你的情况下不可能发生,但其他更复杂的情况呢?可以想象,最后一个表达式可以从对移动对象的引用中读取.
在return <identifier>;案件中这样做要困难得多.