1

在下面的代码中,class B包含一个成员类数组class A
B::A有一个成员bool和一个成员std::thread
下面的代码编译得很好:

// 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.

$

为什么引入 std::mutex 会B::A引入以下编译错误?

// 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;
     ^~~~~

如果我正确理解编译错误,它会抱怨如果B::A没有明确构造B::A::mutex_. 但如果这是真的,我不明白为什么这是必要的:std::mutex有一个默认构造函数,所以不需要任何构造函数参数,如下所示:

// main.cpp
#include <mutex>

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

  return 0;
}

请帮助我了解上述编译错误的性质,以及适当的修复方法。


更新:@Jarod42 和@chris 似乎发现这是一个编译器错误。我正在更新这个问题,询问是否有人可以解释这个错误的性质——初始化成员数组对象元素似乎是一件简单而基础的事情。什么类型的对象会触发此错误,为什么?我无法想象这可能是一个普遍/易于重现的问题......?


更新:一个不太好的解决方法似乎是创建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
$
4

1 回答 1

1

该错误的明显可能原因是 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)

在这里,bare1转换A为无法复制/移动的临时对象,而尽管有“复制”一词,但仍{1}用于初始化Ultimate 。A这种区别在 C++17 中消失了,在 C++17 中,从纯右值初始化(在转换时b或在转换后)a调用纯右值的构造函数(“强制复制省略”)。

这个问题在 C++11 之前也不会出现,因为非静态数组成员只能被默认或值初始化。(={}当时也被认为是正常的复制初始化,并且根本不适用于类对象。)这就是您的解决方法有效的原因,并且逐渐采用新的初始化程序可能是编译器错误持续如此长时间的原因。

请注意,尽管错误提到了std::mutex的已删除复制构造函数,但重要的是它的不可移动性(如 所示B::A::A(B::A&&)),这就是它与std::thread.

于 2021-01-29T04:06:59.413 回答