3

我有一些看起来像这样的代码:

ComplexObject cpy;
{
  RAIILockObject _(obj->mutex);
  cpy = obj->org;
}
// use cpy

为了论证起见,假设默认构造函数的ComplexObject成本很高。

  • C++ 编译器可以(并且可以)用复制构造函数替换 cpy 的默认构造/赋值吗?
  • 有没有办法重组代码以强制优化,同时保留两个本地对象的范围?

编辑:我真的在寻找一个通用的解决方案来解决想要将 RAII 对象与其他东西不正确嵌套的问题。

Konrad Rudolph的解决方案有何评论?

ComplexObject = LockedInitInPlace(obj->org, obj->mutex);

template<class C> C LockedInitInPlace(C& c, Mutex& m) {
    RAIILockObject _(m);
    return c;
}

编辑2:

原始代码具有以下顺序:

  1. 默认构造cpy
  2. 构建 RAIIlock
  3. 分配(复制)现有对象cpy
  4. 破坏lock
  5. 利用cpy
  6. 破坏cpy

我想要的是:

  1. 构建 RAIIlock
  2. 构造cpy(在这种情况下,通过使用现有对象的复制构造函数)。
  3. 破坏lock
  4. 利用cpy
  5. 破坏cpy
4

3 回答 3

3

除非编译器可以向自己证明这种优化会导致相同的行为,否则不会。我真的无法想象编译器可以做到这一点的情况(给定互斥锁)。

这听起来很明显,但是您可以将默认构造函数更改为昂贵吗?如果这些构造函数很容易被意外调用,它们很可能会在其他地方导致性能问题。

或者,您将不得不使用堆和指针(通过复制构造创建)而不是本地实例。

std::scoped_ptr<ComplexObject> cpyPtr = 0;
{
  RIAALockObject _(obj->mutex);
  cpyPtr = new ComplexObject(obj->org);
}
ComplexObject& cpy = *cpyPtr;  // create alias for ease of use.
于 2011-07-20T15:58:34.113 回答
3

如果构造函数很复杂,则不太可能避免使用默认构造函数。

只要程序的可观察行为保持不变,编译器几乎可以做任何事情。

解决此问题的最佳方法是不要使默认构造函数变得ComplexObject昂贵。出于这个原因,使用昂贵的默认构造函数是不好的做法。

于 2011-07-20T16:00:07.800 回答
3

C++ 编译器可以(并且可以)用复制构造函数替换 cpy 的默认构造/赋值吗?

不,编译器被禁止这样做(假设你的类的默认构造函数足够复杂,编译器无法证明它的省略会导致一个等效的程序)。任何不符合标准的编译器。

编辑:以下解决方案有缺陷!不要使用它!

以下解决方案隐藏了竞争条件。如果你的锁是为了确保复制发生在关键部分,那么我的“解决方案”将打破这个假设,因为复制很可能(并且可能会)发生这个关键部分之外。它在您从事其他工作时才有效。但是在您的原始代码中,只有在复制本身很关键时,互斥锁才有意义。

只需执行以下操作以防止默认构造:

ComplexObject = init(any_params_here);

ComplexObject init(any_params_here) {
    RAIILockObject _(obj->mutex);
    return obj->org;
}

由于复制省略,这甚至不会执行不必​​要的复制,只会执行一个(就像在您的代码中一样,但作为直接复制而不是复制分配)。

于 2011-07-20T16:11:34.600 回答