Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
112 views
in Technique[技术] by (71.8m points)

c++ - 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.
B::A has one member bool and one member 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?

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

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

// 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
$
question from:https://stackoverflow.com/questions/65947612/why-does-introducing-stdmutex-to-member-class-generate-this-compile-error

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The apparent, likely cause of the bug is a subtle difference between copy-initialization and 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”. 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”).

The issue also couldn’t arise prior to C++11, since non-static array members could only be default- or value-initialized. (={} 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.

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...