C++自动变量销毁前后创建返回值?

25

在C++中,返回值是否保证在函数内的自动变量被销毁之前创建?请注意Basket::get:

class Basket
{
public:
  // Gift is a struct containing safely copyable things like int or string
  Gift gift;
  // Used to protect access and changes to gift
  Mutex mutex;

  // Copy gift into present, while locked to be thread safe
  void put (const Gift & gift)
  {
    Lock lock(mutex);   // Constructor locks, destructor unlocks mutex
    this->gift = gift;  // Gift assignment operator
  }

  // Return a memberwise-copy of gift, tries to be thread safe (but is it?)
  Gift get ()
  {
    Lock lock(mutex);  // Constructor locks, destructor unlocks mutex
    return gift;       // Gift copy constructor
  }
};

我希望Basket::get在锁对象被销毁之前执行其Gift复制构造函数(返回的临时对象)以避免由于对put的同时调用而导致返回的gift对象被破坏。

我的测试表明,Gift的拷贝确实是在锁被销毁之前创建的,但这是否有保证呢?如果没有,我需要在函数内部创建第二个临时对象,例如:

  Gift get ()
  {
    Gift result;
    {
      Lock lock(mutex);
      result = gift;
    }
    return result;
  }
2个回答

21

是的,auto变量将会一直保持在作用域内,直到return执行结束。如果你使用的是一个优化了return的编译器,这个特性尤为明显。


Gift get() 
{ 
    Lock lock(mutex);
    return gift;
} 

Gift g = basket.get();

这个序列等价于:

Gift g;
Lock lock(mutex);
g = Gift(gift);
lock.~Lock();

可能优化以更像这样的行为:

void get(Gift &ret) 
{ 
    Lock lock(mutex);
    ret = gift;
} 

Gift g;
basket.get(g);

以下序列等效于此:

Gift g;
Lock lock(mutex);
g = gift;
lock.~Lock();
换句话说,return 期间可以删除临时变量。

这正是我需要的答案,关于在返回rvalue后销毁互斥锁的情况。谢谢。 - Dana M
因此,如果这个特性依赖于编译器优化返回值,那么就不能保证其可靠性。为什么要假设编译器会优化呢?假设编译器会优化是不安全的。 - user3091673

6

这是有保障的。在销毁发生之前,返回值将被复制(如果需要)。下面是一个类似的问题/答案,它给出了一个很好的顺序描述。

C++中的作用域和返回值


1
C++标准中并不存在所谓的寄存器(register)。虽然有register关键字,但它早已失去了意义。此外,大多数对象根本无法适应单个寄存器。 - fredoverflow
如果返回的是一个本地对象,那么在它被销毁之前必须先复制。但这不是这种情况。我有一个类成员数据正在返回中,因此复制发生的确切时间语义似乎有所不同。 - maxpolk
@maxpolk 你正在通过值返回成员。除非编译器可以优化为引用赋值(如Remy所示),否则将会有一次复制。如果您返回的是引用或指针,则锁定仍将在之后被销毁。 - AJG85
@FredOverflow - 完全正确。我当时在想汇编语言,但即使如此,我的措辞也不够严谨。我想要表达的主要观点是,在堆栈展开之前会发生复制。虽然已经有人提到过了,但这也可以通过“如果必要”的方式进行限定。我会编辑答案。 - scotinus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接