像这样的代码经常出现在r值引用文章中:
戴夫艾布拉姆斯:用右值参考来移动它
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类实现?
如果允许这样的优化,它是否由某些编译器在实践中执行?
不,请考虑:
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
。
可以 编译器做到了吗?仅作为显式语言扩展,因为标准不允许它们在没有它的情况下进行这样的优化。
应该 他们这样做?没有。的意思 g(b)
应该基于。的定义 g
和 b
。 g
应该是一些可调用的类型,它有一个过载,需要一些东西 b
可以隐式转换成。给定访问所有可用的定义 g
s,以及。的定义 b
,你应该能够确定 究竟 将调用什么函数。
现在允许这种“优化”意味着这是不可能的。 g(b)
威力 执行移动,它可能不会,具体取决于具体位置 g(b)
碰巧是在一个功能。这不是一件好事。
return
被允许逃脱它,但只是因为它仍然具有相同的含义。 return b
将永远尝试从 b
如果 b
是一个值类型,其生命周期仅限于函数的范围。
(...)假设一个通用的情况,编译器不知道如何为X类实现移动,复制或破坏?
不,编译器不允许根据信仰进行优化。
为清楚起见,这个问题与复制省略无关:可以允许编译器删除副本,但是他们不能无限制地将副本更改为移动。