我有一些代码看起来像这样:
ComplexObject cpy;
{
RAIILockObject _(obj->mutex);
cpy = obj->org;
}
// use cpy
为了便于讨论,假设ComplexObject
的默认构造函数是昂贵的。
- c++编译器可以(并且可以)用复制构造函数替换copy的默认构造/赋值吗?是否有任何方法重构代码,以便强制优化,同时保留两个局部对象的作用域?
编辑:我真的在寻找一个通用的解决方案,以希望RAII对象与其他东西嵌套不当的问题。
对Konrad Rudolph的解决方案有什么评论吗?
ComplexObject = LockedInitInPlace(obj->org, obj->mutex);
template<class C> C LockedInitInPlace(C& c, Mutex& m) {
RAIILockObject _(m);
return c;
}
编辑2:
原始代码有这样的序列:
- 默认结构
cpy
- construct RAII
lock
- 分配(复制)现有对象给
cpy
销毁 - 使用
cpy
销毁
lock
cpy
我要的是:
- construct RAII
lock
- 构建
cpy
(在这种情况下,通过使用现有对象的复制构造函数)。销毁lock
- 使用
cpy
销毁
cpy
除非编译器可以向自己证明这种优化会导致相同的行为,否则不会。我真的无法想象(给定互斥锁)编译器可以做到这一点。
这可能听起来很明显,但是您可以将默认构造函数更改为而不是吗?如果这样的构造函数很容易被意外调用,则很可能在其他地方导致性能问题。或者你将不得不使用堆和指针(通过复制构造创建)而不是本地实例。
std::scoped_ptr<ComplexObject> cpyPtr = 0;
{
RIAALockObject _(obj->mutex);
cpyPtr = new ComplexObject(obj->org);
}
ComplexObject& cpy = *cpyPtr; // create alias for ease of use.
如果构造函数很复杂,则不太可能避免使用默认构造函数。
编译器可以做几乎任何事情,只要你的程序的可观察行为保持不变。
解决此问题的最佳方法是不要使ComplexObject
的默认构造函数昂贵。由于这个原因,使用昂贵的默认构造函数是不好的做法。
c++编译器可以(并且可以)用复制构造函数替换copy的默认构造/赋值吗?
不,编译器被禁止这样做(假设你的类的默认构造函数足够复杂,以至于编译器无法证明省略它会产生一个等效的程序)。任何这样做的编译器都不符合标准。
编辑:下面的解决方案是有缺陷的!不要使用它!
下面的解决方案隐藏了一个竞争条件。如果你的锁是为了确保复制发生在临界区,那么我的"解决方案"将打破这个假设,因为复制很可能(而且很可能)发生在这个临界区之外。只有当你在做其他工作时,才会起作用。但是在原始代码中,互斥锁只有在复制本身是关键的情况下才有意义。
只需执行以下操作以防止默认构造:
ComplexObject = init(any_params_here);
ComplexObject init(any_params_here) {
RAIILockObject _(obj->mutex);
return obj->org;
}
由于复制省略,这甚至不会执行不必要的复制,只需一个(就像在您的代码中一样,但作为直接复制而不是复制赋值)。