代码意外编译失败。为什么?
Code unexpectedly fails to compile. Why?
看看下面的代码示例,它使用类似于 boost::noncopyable
的 class uncopiable
:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) = default;
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {}
因为我只想让内部 class 移动,所以我明确指定它的移动构造函数和赋值运算符为默认值,而且因为我听说指定所有 "special member functions" 在这种情况下我从 uncopiable
继承了它。问题是每个编译器的编译都失败,并显示类似于以下错误消息的内容(此消息摘自 clang 消息):
/usr/include/c++/v1/memory:1645:31: error: call to implicitly-deleted copy constructor of 'A::B'
...
main.cpp:26:10: note: in instantiation of function template specialization 'std::__1::vector >::emplace_back<>' requested here
main.cpp:19:3: note: copy constructor is implicitly deleted because 'B' has a user-declared move constructor
可以通过删除继承来修复它(仍然不会创建复制操作)。但是在那之后在 class 中写入要显式删除的复制操作也是可以的。
我的问题是:为什么会这样?通过继承 helper classes 来禁用 constructors/assignment 运算符是否可以被认为是一个缺陷?
问题是你的uncopiable
class不能移动。因此派生的 class 的 default
移动构造函数/赋值运算符尝试使用 delete
d 复制版本。
static_assert(std::is_move_constructible<uncopiable>::value, ""); // fails
static_assert(std::is_move_assignable<uncopiable>::value, ""); // fails
原因是 § 12.8 ¶ 9:
If the definition of a class X
does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X
does not have a user-declared copy constructor,
X
does not have a user-declared copy assignment operator,
X
does not have a user-declared move assignment operator, and
X
does not have a user-declared destructor.
将复制运算符或赋值运算符声明为 delete
d 仍然算作声明。
解决方案当然是为uncopiable
声明移动操作。
uncopiable(uncopiable&&) noexcept = default;
uncopiable& operator=(uncopiable&&) noexcept = default;
注意move operations should usually be declared noexcept
。特别是如果您想像示例中那样在 std::vector
中使用类型。
这在 MinGw 上编译正常:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) {};
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {
A* a = new A();
}
看看下面的代码示例,它使用类似于 boost::noncopyable
的 class uncopiable
:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) = default;
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {}
因为我只想让内部 class 移动,所以我明确指定它的移动构造函数和赋值运算符为默认值,而且因为我听说指定所有 "special member functions" 在这种情况下我从 uncopiable
继承了它。问题是每个编译器的编译都失败,并显示类似于以下错误消息的内容(此消息摘自 clang 消息):
/usr/include/c++/v1/memory:1645:31: error: call to implicitly-deleted copy constructor of 'A::B'
...
main.cpp:26:10: note: in instantiation of function template specialization 'std::__1::vector >::emplace_back<>' requested here
main.cpp:19:3: note: copy constructor is implicitly deleted because 'B' has a user-declared move constructor
可以通过删除继承来修复它(仍然不会创建复制操作)。但是在那之后在 class 中写入要显式删除的复制操作也是可以的。
我的问题是:为什么会这样?通过继承 helper classes 来禁用 constructors/assignment 运算符是否可以被认为是一个缺陷?
问题是你的uncopiable
class不能移动。因此派生的 class 的 default
移动构造函数/赋值运算符尝试使用 delete
d 复制版本。
static_assert(std::is_move_constructible<uncopiable>::value, ""); // fails
static_assert(std::is_move_assignable<uncopiable>::value, ""); // fails
原因是 § 12.8 ¶ 9:
If the definition of a class
X
does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X
does not have a user-declared copy constructor,X
does not have a user-declared copy assignment operator,X
does not have a user-declared move assignment operator, andX
does not have a user-declared destructor.
将复制运算符或赋值运算符声明为 delete
d 仍然算作声明。
解决方案当然是为uncopiable
声明移动操作。
uncopiable(uncopiable&&) noexcept = default;
uncopiable& operator=(uncopiable&&) noexcept = default;
注意move operations should usually be declared noexcept
。特别是如果您想像示例中那样在 std::vector
中使用类型。
这在 MinGw 上编译正常:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) {};
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {
A* a = new A();
}