c++ - 复制省略可以在 synchronize-with 语句中发生吗?

标签 c++ c++11

在下面的示例中,如果我们暂时忽略互斥锁,复制省略可能会消除对复制构造函数的两次调用。

user_type foo()
{
  unique_lock lock( global_mutex );
  return user_type(...);
}

user_type result = foo();

现在复制省略的规则没有提到线程,但我想知道它是否真的应该跨越这样的界限。在上述情况下,逻辑抽象机器间线程中的最终拷贝发生在互斥锁释放之后。但是,如果省略拷贝,则结果数据结构会在互斥锁中初始化,因此它在互斥锁释放之前发生在线程间。

我还没有想到一个具体的例子,复制省略如何真正导致竞争条件,但内存序列中的干扰似乎是个问题。任何人都可以明确地说它不会导致问题,或者有人可以提出一个确实可以打破的例子吗?


为确保答案不仅仅针对特殊情况,请注意,如果我有类似 new (&result)( foo() )。也就是说,result 不需要是堆栈对象。 user_type 本身也可以处理线程间共享的数据。


答案:我选择第一个答案作为最相关的讨论。基本上,由于标准说可能会发生省略,因此程序员在跨越同步边界时必须小心。没有迹象表明这是有意还是无意的要求。我们仍然缺乏任何示例来说明可能出现的问题,因此无论哪种方式都可能不是问题。

最佳答案

线程与它无关,但锁的构造函数/析构函数的顺序可能会影响你。

查看您的代码执行的低级步骤,没有复制省略,一一(使用 GCC 选项 -fno-elide-constructors):

  1. 构造
  2. 使用 (...) 参数构造临时 user_type
  3. 使用步骤 2 中的值复制构造函数的临时返回值,类型为 user_type
  4. 销毁第 2 步中的临时文件。
  5. 销毁
  6. 使用步骤 3 中的值复制构造 user_type 结果
  7. 销毁第 3 步中的临时文件。
  8. 稍后,销毁 result

自然,通过多次复制省略优化,它只是:

  1. 构造
  2. 直接用(...)构造result对象。
  3. 销毁
  4. 稍后,销毁 result

请注意,在这两种情况下,带有 (...)user_type 构造函数都受到锁的保护。任何其他复制构造函数或析构函数调用可能不 protected 。

事后思考:

我认为最有可能导致问题的地方是析构函数。也就是说,如果您使用 (...) 构造的原始对象处理任何共享资源的方式与其拷贝不同,并且在需要锁的析构函数中执行某些操作,那么您就有问题了。

当然,这意味着您的对象一开始就设计得很糟糕,因为拷贝的行为与原始对象不同。

引用:

在 C++11 草案中,12.8.31(没有所有“移动”的类似措辞在 C++98 中:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

  • a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object can be omitted by constructing the automatic object directly into the exception object

  • when a temporary class object that has not been bound to a reference would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

  • when the exception-declaration of an exception handler declares an object of the same type (except for cv-qualification) as the exception object, the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.

第 1 点和第 3 点在您的示例中协作以省略所有拷贝。

关于c++ - 复制省略可以在 synchronize-with 语句中发生吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8924034/

相关文章:

c++ - 编写支持新旧 C++ 编译器的代码?

c++ - Thread C++ 成员函数模板可变参数模板

c++ - 使用析构函数后,代码显示 "reference to Book::~Book()"错误

c++ - 如何在 C++ 中使用 [[noreturn]] 属性?

C++ 在函数前使用 extern

c++ - 将 S3TC/DXTn 数据转换为 QImage

c++ - 如何从对象内部的 typedef 获取模板参数类型

c++11 类内成员初始化

c++ - 如何向后读取文件以有效地查找子字符串

C++ 在构造函数中初始化未知大小的 2DimArray