简体   繁体   English

为什么将 std::mutex 引入成员 class 会产生此编译错误?

[英]Why does introducing std::mutex to member class generate this compile error?

In the code below, class B contains an array of member class class A .在下面的代码中, class B包含成员 class class A的数组。
B::A has one member bool and one member std::thread . B::A有一个成员bool和一个成员std::thread
The code below compiles fine:下面的代码编译得很好:

// main.cpp
#include <mutex>
#include <thread>

class B {
public:
  B();

private:

  class A {
    public:
      A( const bool& b ) : b_( b ) {}

      bool b_;
      std::thread thread_;
  } a_[2];
};

B::B() : a_{ { false }, { false } } { }

int main( int argc, char* argv[] ) {
  B b;

  return 0;
}
$ g++ --version && g++ -g ./main.cpp
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$

Why does introducing a std::mutex to B::A introduce the following compile error?为什么向B::A引入 std::mutex 会引入以下编译错误?

// main.cpp
#include <mutex>
#include <thread>

class B {
public:
  B();

private:

  class A {
    public:
      A( const bool& b ) : b_( b ) {}

      bool b_;
      std::mutex mutex_;  // I break compilation!
      std::thread thread_;
  } a_[2];
};

B::B() : a_{ { false }, { false } } { }

int main( int argc, char* argv[] ) {
  B b;

  return 0;
}
$ g++ -g ./main.cpp
./main.cpp: In constructor ‘B::B()’:
./main.cpp:21:35: error: use of deleted function ‘B::A::A(B::A&&)’
 B::B() : a_{ { false }, { false } } { }
                                   ^
./main.cpp:11:9: note: ‘B::A::A(B::A&&)’ is implicitly deleted because the default definition would be ill-formed:
   class A {
         ^
./main.cpp:11:9: error: use of deleted function ‘std::mutex::mutex(const std::mutex&)’
In file included from /usr/include/c++/6/mutex:44:0,
                 from ./main.cpp:2:
/usr/include/c++/6/bits/std_mutex.h:97:5: note: declared here
     mutex(const mutex&) = delete;
     ^~~~~

If I correctly understand the compile error, it's complaining that an instance of B::A cannot be created without explicit construction of B::A::mutex_ .如果我正确理解编译错误,它抱怨B::A的实例无法在没有B::A::mutex_的显式构造的情况下创建。 But if this is true, I don't understand why this should be necessary: std::mutex has a default constructor, so doesn't need any constructor arguments, as demonstrated below:但如果这是真的,我不明白为什么必须这样做: std::mutex有一个默认构造函数,所以不需要任何构造函数 arguments,如下所示:

// main.cpp
#include <mutex>

int main( int argc, char* argv[] ) {
  std::mutex mutex[10];

  return 0;
}

Please help me understand the nature of the above compile error, and what an appropriate fix might be.请帮助我了解上述编译错误的性质,以及适当的修复方法。


Update : @Jarod42 and @chris seem to have discovered this is a compiler bug.更新:@Jarod42 和@chris 似乎发现这是一个编译器错误。 I'm updating the question to ask if anyone could explain the nature of this bug -- initiaizing member array-of-object elements seems like such a simple and foundational thing.我正在更新这个问题,询问是否有人可以解释这个错误的性质——初始化成员数组对象元素似乎是一件简单而基础的事情。 What type of objects trigger this bug and why?什么类型的对象会触发此错误,为什么? I can't imagine this could be a universal/easily reproducible problem...?我无法想象这可能是一个普遍/易于重现的问题......?


Update: A not-great workaround seems to be making B::A::A an empty constructor and initializing B::A::b_ with an rvalue.更新:一个不太好的解决方法似乎是使B::A::A成为一个空的构造函数并用右值初始化B::A::b_ :( :(

// main.cpp
#include <mutex>
#include <thread>

class B {
public:
  B();

private:

  class A {
    public:
      A() : b_( false ) {}

      bool b_;
      std::mutex mutex_;
      std::thread thread_;
  } a_[2];
};

B::B() { }

int main( int argc, char* argv[] ) {
  B b;

  return 0;
}
$ g++ -g ./main.cpp
$

The apparent, likely cause of the bug is a subtle difference between copy-initialization and copy-list-initialization :该错误的明显可能原因是 copy-initialization 和copy-list-initialization之间的细微差别:

struct A {
  A(int);
  A(A&&)=delete;
} a=1,         // error: not movable
  b=A(1),      // error
  c={1},       // OK, no temporary constructed
  d[]={1},     // error
  e[]={A{1}},  // error
  f[]={{1}};   // OK (the compiler bug)

Here a bare 1 is converted to a temporary A which cannot be copied/moved, whereas {1} is used to initialize the ultimate A despite the term “copy”.这里一个裸1转换为一个不能被复制/移动的临时A ,而{1}用于初始化最终A尽管有术语“复制”。 This distinction vanishes in C++17, where initialization from a prvalue (as for b or a after conversion) invokes only the prvalue's constructor (“mandatory copy elision”).这种区别在 C++17 中消失了,其中从纯右值初始化(对于b或转换后的a调用纯右值的构造函数(“强制复制省略”)。

The issue also couldn't arise prior to C++11, since non-static array members could only be default- or value-initialized.在 C++11 之前也不会出现此问题,因为非静态数组成员只能默认或值初始化。 ( ={} was also considered normal copy-initialization then and didn't apply to class objects at all.) This is why your workaround worked, and the gradual adoption of the new initializers is probably why the compiler bug lasted as long as it did. ={}当时也被认为是正常的复制初始化,并且根本不适用于 class 对象。)这就是您的解决方法有效的原因,并且逐渐采用新的初始化程序可能是编译器错误持续的原因做过。

Note that, despite the error mentioning std::mutex 's deleted copy constructor, it is its non- movability that matters (as indicated by the B::A::A(B::A&&) ), which is how it differs from std::thread .请注意,尽管错误提到std::mutex的已删除复制构造函数,但重要的是它的不可移动性(如B::A::A(B::A&&)所示),这就是它的不同之处来自std::thread

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM