C ++中变量作用域的嵌套不正确?

时间:2011-07-20 15:53:07

标签: c++ optimization raii scoping

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

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. 构建RAII lock
  3. 将现有对象分配(复制)到cpy
  4. 破坏lock
  5. 使用cpy
  6. 破坏cpy
  7. 我想要的是:

    1. 构建RAII lock
    2. 构造cpy(在本例中是使用现有对象的复制构造函数)。
    3. 破坏lock
    4. 使用cpy
    5. 破坏cpy

3 个答案:

答案 0 :(得分:3)

除非编译器能够向自己证明这种优化导致相同的行为,否则不行。我无法想象编译器可以执行此操作的情况(给定互斥锁)。

这听起来很明显,但是你可以将默认构造函数更改为而不是代价高昂吗?如果这些构造函数容易被意外调用,则可能会在其他地方引起性能问题。

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

std::scoped_ptr<ComplexObject> cpyPtr = 0;
{
  RIAALockObject _(obj->mutex);
  cpyPtr = new ComplexObject(obj->org);
}
ComplexObject& cpy = *cpyPtr;  // create alias for ease of use.

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:3)

  

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

不,禁止编译器执行此操作(假设您的类的默认构造函数足够复杂,以至于编译器无法证明其省略将导致等效程序)。任何编译器都不符合标准。

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

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

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

ComplexObject = init(any_params_here);

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

感谢copy elision,这甚至不会执行不必​​要的副本,只需一个(就像在您的代码中一样,但是作为直接复制而不是复制分配)。