线程与此无关,但锁的构造/析构函数顺序可能会影响您。通过查看代码执行的低级步骤(使用GCC选项-fno-elide-constructors),一次一个地进行,没有复制省略:
- 构造锁。
- 使用(...)参数构造临时用户类型。
- 使用步骤2中的值复制构造函数的临时返回值,类型为用户类型。
- 销毁步骤2中的临时对象。
- 销毁锁。
- 使用步骤3中的值复制构造用户类型结果。
- 销毁步骤3中的临时对象。
- 以后,销毁结果。
自然地,通过多个复制省略优化,它将仅仅是:
- 构造锁。
- 直接使用(...)构造result对象。
- 销毁锁。
- 以后,销毁result。
请注意,在两种情况下,具有(...)的user_type构造函数都受锁的保护。任何其他复制构造函数或析构函数调用可能不受保护。
思考:
我认为最可能引起问题的地方是在析构函数中。也就是说,如果您最初使用(...)构造的对象与其副本不同地处理任何共享资源,并且在析构函数中执行需要锁定的操作,则会出现问题。
自然而然地,这意味着您的对象首先设计得很糟糕,因为副本的行为与原始对象不同。
参考文献:
在C++11草案中,12.8.31(没有所有“移动”的类似措辞在C++98中:
当特定条件满足时,实现允许省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在那两个对象将被销毁的时间中较晚的时间点上,而不进行优化。这种称为复制省略的复制/移动操作可以在以下情况下执行(这些情况可以组合以消除多个副本):
1. 在具有类返回类型的函数中的return语句中,当表达式是非易失自动对象(函数或catch子句参数之外的其他非易失自动对象)的名称,并且具有与函数返回类型相同的cv-未限定类型时,可以通过直接将自动对象构造到函数的返回值中来省略复制/移动操作。
2. 一个函数或catch子句参数),其作用域不延伸到最内层try块的末尾(如果有),则可以通过将自动对象直接构造到异常对象中来省略从操作数到异常对象的复制/移动操作。
3. 当将尚未绑定到引用的临时类对象复制/移动到具有相同cv-未限定类型的类对象时,可以通过直接将临时对象构造到省略的复制/移动操作的目标中来省略复制/移动操作。
4. 当异常处理程序的异常声明声明与异常对象相同类型(除了cv-限定)的对象时,如果程序的含义除了执行由异常声明声明的对象的构造函数和析构函数外不会更改,则可以通过将异常声明视为对异常对象的别名来省略复制/移动操作。
在您的示例中,点1和点3合作以省略所有副本。
bar
而不是其他名称吗? - ta.speot.is&result
传递给foo
,并确保~unique_lock
使用result
的别名,以便我们的程序始终使用锁修改result
。 - Steve Jessopnew (*result)(foo())
?鉴于&result
,我不明白它除了你所说的“堆栈对象”以外还可以是什么(实际上自动存储期与静态存储期的复杂性不容忽视)。 - Lightness Races in Orbit