为什么向成员class引入std::mutex会产生这个编译错误?
Why does introducing std::mutex to member class generate this compile error?
在下面的代码中,class B
包含一个成员数组 class 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 似乎已经发现这是一个编译器错误。我正在更新这个问题,询问是否有人可以解释这个错误的性质——初始化成员对象数组元素似乎是一件如此简单和基础的事情。什么类型的对象触发这个错误,为什么?我无法想象这可能是一个 universal/easily 可重现的问题...?
更新: 一个不太好的解决方法似乎是使 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
$
该错误的明显可能原因是复制初始化和 复制列表初始化:
之间的细微差别
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)
这里一个裸1
被转换为临时A
不能copied/moved,而{1}
被使用初始化 最终 A
尽管术语“复制”。这种区别在 C++17 中消失了,在 C++17 中,从纯右值初始化(对于 b
或 a
转换后)调用 only 纯右值的构造函数(“强制复制省略”).
在 C++11 之前也不会出现此问题,因为非静态数组成员只能进行默认或值初始化。 (={}
当时也被认为是正常的复制初始化,根本不适用于 class 对象。)这就是您的解决方法起作用的原因,并且逐渐 采用 的新初始化程序可能是编译器错误持续这么久的原因。
请注意,尽管错误提到 std::mutex
已删除 copy 构造函数,但它的非 movability重要(如 B::A::A(B::A&&)
所示),这就是它与 std::thread
.
的不同之处
在下面的代码中,class B
包含一个成员数组 class 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 似乎已经发现这是一个编译器错误。我正在更新这个问题,询问是否有人可以解释这个错误的性质——初始化成员对象数组元素似乎是一件如此简单和基础的事情。什么类型的对象触发这个错误,为什么?我无法想象这可能是一个 universal/easily 可重现的问题...?
更新: 一个不太好的解决方法似乎是使 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
$
该错误的明显可能原因是复制初始化和 复制列表初始化:
之间的细微差别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)
这里一个裸1
被转换为临时A
不能copied/moved,而{1}
被使用初始化 最终 A
尽管术语“复制”。这种区别在 C++17 中消失了,在 C++17 中,从纯右值初始化(对于 b
或 a
转换后)调用 only 纯右值的构造函数(“强制复制省略”).
在 C++11 之前也不会出现此问题,因为非静态数组成员只能进行默认或值初始化。 (={}
当时也被认为是正常的复制初始化,根本不适用于 class 对象。)这就是您的解决方法起作用的原因,并且逐渐 采用 的新初始化程序可能是编译器错误持续这么久的原因。
请注意,尽管错误提到 std::mutex
已删除 copy 构造函数,但它的非 movability重要(如 B::A::A(B::A&&)
所示),这就是它与 std::thread
.