编译器是否足够聪明,以至于 std::move 变量超出范围?
Are compilers clever enough to std::move variables going out of scope?
考虑以下代码段:
std::vector<int> Foo() {
std::vector<int> v = Bar();
return v;
}
return v
是 O(1(,因为 NRVO 将省略副本,直接在存储中构造v
,否则函数的返回值将被移动或复制到其中。现在考虑功能类似的代码:
void Foo(std::vector<int> * to_be_filled) {
std::vector<int> v = Bar();
*to_be_filled = v;
}
这里可以提出类似的论点,因为可以想象*to_be_filled = v
编译为 O(1( 移动分配,因为它是一个超出范围的局部变量(编译器应该很容易验证v
在这种情况下没有外部引用,从而在最后一次使用时将其提升为右值(。是这样吗?有没有一个微妙的原因不呢?
此外,感觉这种模式可以扩展到左值超出范围的任何上下文:
void Foo(std::vector<int> * to_be_filled) {
if (Baz()) {
std::vector<int> v = Bar();
*to_be_filled = v;
}
...
}
期望编译器找到诸如*to_be_filled = v
之类的模式,然后自动优化它们以假定右值语义,是否/可以/是否有用/合理?
编辑:
g++ 7.3.0 不会在-O3 模式下执行任何此类优化。
不允许编译器任意决定将左值名称转换为要从中移动的右值。它只能在C++标准允许的情况下这样做。例如在return
语句中(并且仅当其return <identifier>;
时(。
*to_be_filled = v;
将始终执行复制。即使它是可以访问v
的最后一个语句,它也始终是一个副本。不允许编译器更改它。
我的理解是返回 v 是 O(1(,因为 NRVO 将(实际上(将 v 变成一个 rvalue,然后利用 std::vector 的移动构造函数。
这不是它的工作原理。NRVO将完全消除移动/复制。但是,return <identifier>;
成为右值的能力并不是"优化"。实际上,编译器将它们视为右值是一项要求。
编译器可以选择复制省略。编译器无法选择return <identifier>;
做什么。因此,上述内容要么根本不移动(如果发生 NRVO(,要么会移动对象。
有没有一个微妙的原因不呢?
不允许这样做的一个原因是,语句的位置不应任意更改该语句正在执行的操作。看,return <identifier>;
将始终从标识符移动(如果它是局部变量(。它在函数中的位置并不重要。由于是一个return
语句,我们知道如果执行return
,则不会执行任何内容。
武断的陈述并非如此。表达式的行为不应*to_be_filled = v;
根据它在代码中的位置而更改。您不应该仅仅因为向函数添加了另一行而将移动转换为副本。
另一个原因是任意语句很快就会变得非常复杂。return <identifier>;
非常简单;它将标识符复制/移动到返回值并返回。
相比之下,如果你有一个对v
的引用,会发生什么,并且to_be_filled
以某种方式使用它。当然,这不会发生在您的案例中,但是其他更复杂的案例呢?可以想象,最后一个表达式可以从对移自对象的引用中读取。
在return <identifier>;
的情况下,要做到这一点要困难得多。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 将数组的地址分配给变量并删除
- 为"adjacent"变量赋值时出现问题
- enum是C++中的宏变量还是整数变量
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- std::move 如何使原始变量的值无效?
- 作为副本传递的 std::move()'d 变量的效果是什么?
- 编译器是否足够聪明,以至于 std::move 变量超出范围?
- std::move() 或其在局部变量上的显式等价物可以允许 elision 吗?
- 是否可以 std::move 局部堆栈变量?
- 移动变量是否有效,可以在 std::move 之后使用
- 如果我有一个向量(或类似的东西)成员变量,那么move构造函数看起来怎么样
- STD :: MOVE或STD ::向前转向C 中的成员变量时
- 将局部变量声明为右值引用是没有用的,例如T&& r = move(v)?
- std::move on 一个已经是 T&& 的变量
- 为什么throw局部变量调用move构造函数
- std::move更改变量地址
- 当移动局部变量时,我应该使用std::move吗?