Edw*_*per 25 c++ optimization rvalue move-semantics c++11
如果编译器可以证明左值不会再次使用,它是否可以进行自动左值到右值转换?这是一个澄清我的意思的例子:
void Foo(vector<int> values) { ...}
void Bar() {
vector<int> my_values {1, 2, 3};
Foo(my_values); // may the compiler pretend I used std::move here?
}
Run Code Online (Sandbox Code Playgroud)
如果将a std::move添加到注释行,则可以将矢量移动到Foo参数中,而不是复制.但是,正如所写,我没有使用std::move.
静态地证明my_values在注释行之后不会被使用,这很容易.那么编译器允许移动向量,还是需要复制它?
Yak*_*ont 33
如果复制发生vector在调用中,则编译器必须表现为Foo.
如果编译器可以证明存在一个有效的抽象机器行为,没有可观察到的副作用(在抽象机器行为中,而不是在真正的计算机中!),这涉及到移动std::vector进入Foo,它可以做到这一点.
在您的上述情况下,这(移动没有抽象机器可见的副作用)是真的; 但是,编译器可能无法证明它.
复制a时可能出现的可观察行为std::vector<T>是:
int无法观察到这样做std::allocator<>在不同时间调用默认值.这调用::new和::delete(可能是1)在任何情况下,::new并::delete没有在上面的程序中被替换,所以你不能在标准下观察到这一点.T在不同的对象上多次调用析构函数.没有观察到int.vector调用后为非空Foo.没有人检查它,所以它是空的,如果它不是.Foo.虽然您可能会说"但是如果系统内存不足,并且向量很大,那么这是不可观察的?":
抽象机器没有"内存不足"的情况,它只是std::bad_alloc因为非限制性原因而有时会失败(抛出).它没有失败是抽象机器的有效行为,并且没有失败的是不分配(实际)内存(在实际计算机上)也是有效的,只要内存的不存在没有可观察到的副作用.
一个稍微多一点的玩具箱:
int main() {
int* x = new int[std::size_t(-1)];
delete[] x;
}
Run Code Online (Sandbox Code Playgroud)
虽然这个程序明确地分配了太多的内存,但编译器可以自由地不分配任何东西.
我们可以走得更远.甚至:
int main() {
int* x = new int[std::size_t(-1)];
x[std::size_t(-2)] = 2;
std::cout << x[std::size_t(-2)] << '\n';
delete[] x;
}
Run Code Online (Sandbox Code Playgroud)
可以变成std::cout << 2 << '\n';.这个大缓冲区必须抽象地存在,但只要你的"真实"程序表现得像抽象机器一样,它实际上不必分配它.
不幸的是,以任何合理的规模这样做都很困难.信息可以通过C++程序泄漏的方式有很多种.所以依靠这样的优化(即使它们发生)也不会很好.
1 有一些关于合并呼叫的new内容可能会混淆问题,我不确定即使有替换也不合法跳过呼叫::new.
一个重要的事实是,有一些编译器的情况下不表现为,如果有一个副本,即使需要std::move不叫.
当你return从一个看起来像return X;并且X是标识符的行中的函数的局部变量,并且该局部变量具有自动存储持续时间(在堆栈上)时,该操作隐式地是一个移动,并且编译器(如果它可以)可以将返回值和局部变量存在于一个对象中(甚至省略move).
从临时构造对象时也是如此 - 操作隐式地是移动(因为它绑定到右值)并且它可以完全消除移动.
在这两种情况下,编译器都需要将其视为移动(而不是副本),并且它可以忽略移动.
std::vector<int> foo() {
std::vector<int> x = {1,2,3,4};
return x;
}
Run Code Online (Sandbox Code Playgroud)
即x没有std::move,但它被移动到返回值,并且该操作可被省略(x和返回值可以变成一个对象).
这个:
std::vector<int> foo() {
std::vector<int> x = {1,2,3,4};
return std::move(x);
}
Run Code Online (Sandbox Code Playgroud)
阻止elision,就像这样:
std::vector<int> foo(std::vector<int> x) {
return x;
}
Run Code Online (Sandbox Code Playgroud)
我们甚至可以阻止此举:
std::vector<int> foo() {
std::vector<int> x = {1,2,3,4};
return (std::vector<int> const&)x;
}
Run Code Online (Sandbox Code Playgroud)
甚至:
std::vector<int> foo() {
std::vector<int> x = {1,2,3,4};
return 0,x;
}
Run Code Online (Sandbox Code Playgroud)
因为隐含行动的规则是故意脆弱的.(0,x是使用备受诟病的,运营商).
现在,,不建议依赖于在最后一个基础上不发生的隐式移动:标准委员会已经将隐式复制案例更改为隐式移动,因为隐式移动被添加到语言中因为它们认为它无害(其中函数返回A带有A(B&&)ctor 的类型,return语句是return b;where b的类型B;在执行副本的C++ 11版本中,现在它进行了移动.)不能排除进一步扩展隐式移动:显式地转换为a const&可能是现在和未来防止它的最可靠方法.