这样的代码经常出现在r值引用文章中:
Dave Abrams: Move It With Rvalue References
void g(X);
void f()
{
X b;
g(b); // still need the value of b
…
g( std::move(b) ); // all done with b now; grant permission to move
}
编译器是否可以自动生成此优化,即检测l值是否会被破坏并且可能会被移除,或者这将违反标准,假设编译器不知道任何事情的一般情况关于如何为X类实现移动,复制或破坏?
如果允许这样的优化,是否由某些编译器在实践中执行?
答案 0 :(得分:7)
没有。考虑:
using X = std::shared_ptr<int>;
void g(X);
void f() {
X b = std::make_shared<int>();
int &i = *b;
g(b); // last use of 'b'
i = 5;
}
通常,编译器不能假设更改X
的副本,移动和析构函数的语义将是合法的更改,而不对围绕b
的使用的所有代码进行分析(即,整个f
,g
以及其中使用的所有类型。
实际上,在某些情况下,可能需要进行全程序分析:
using X = std::shared_ptr<std::lock_guard<std::mutex>>;
std::mutex i_mutex;
int i;
void g(X);
void f() {
X b = std::make_shared<std::lock_guard<std::mutex>>(i_mutex);
g(b); // last use of 'b'
i = 5;
}
如果移动了b
,则会针对使用i
同步i_mutex
访问权限的其他线程引入数据竞争。
答案 1 :(得分:4)
(...)假设一般情况,编译器不知道如何为X类实现移动,复制或破坏?
不,编制者不得基于信仰进行优化。
为清楚起见,这个问题与复制省略无关:编制者可能会被允许删除副本,但他们不能随意改变副本。
答案 2 :(得分:4)
可以编译器吗?仅作为明确的语言扩展,因为标准不允许它们在没有它的情况下进行这样的优化。
应该他们这样做了吗?不。g(b)
的含义应基于g
和b
的定义。 g
应该是一些可调用类型,它有一个重载,可以隐式转换为b
。如果可以访问所有可用g
的定义以及b
的定义,您应该能够确定完全将调用哪个函数。
允许这样做&#34;优化&#34;现在意味着这是不可能的。 g(b)
可能执行移动,但可能不会,具体取决于g(b)
在函数中的确切位置。这不是一件好事。
return
被允许侥幸逃脱,但仅仅因为它仍具有相同的含义。 return b
将始终尝试从b
移动,前提是b
是一个值生命周期限制的值类型。