[英]Nested union compilation error: C2280 attempting to reference a deleted function
我对Clustering
class 有疑问,其中的联合导致了问题。
我已经尝试了几件事,但仍然没有,检查了操作员,但无法找到错误。
这最初是使用Visual Studio 2008构建的,并且在那里编译得很好。
但是我必须用Visual Studio 2019来编译,编译时遇到了这个问题:
error C2280: 'Clustering<T>::NewType::~NewType(void)': attempting to reference a deleted function
error C2280: with
error C2280: [
error C2280: T=ConsortiaRelationElem
error C2280: ]
我想这个错误是由于 c++ 11 参数的实现造成的。
这是Clustering
class 的定义:
template <typename T>
class Clustering
{
friend CPlayer;
public:
union NewType
{
struct
{
T _Ttype;
};
CPlayer* m_player;
};
NewType m_Data;
public:
Clustering(){};
Clustering(const T& _Ttype)
{
m_Data.m_player = NULL;
memcpy( &m_Data._Ttype, &_Ttype, sizeof(T) );
};
Clustering& operator=(const T& _Ttype)
{
m_Data.m_player = NULL;
memcpy(&m_Data._Ttype, &_Ttype, sizeof(T));
};
~Clustering() { };
};
这是我目前在编译时遇到的所有错误:
warning C4624: 'Clustering<T>::NewType': destructor was implicitly defined as deleted
warning C4624: with
warning C4624: [
warning C4624: T=ConsortiaRelationElem
warning C4624: ]
error C2280: 'Clustering<T>::NewType::~NewType(void)': attempting to reference a deleted function
error C2280: with
error C2280: [
error C2280: T=ConsortiaRelationElem
error C2280: ]
message : 'Clustering<T>::NewType::~NewType(void)': function was implicitly deleted because 'Clustering<T>::NewType' has a variant data member 'Clustering<T>::NewType::_Ttype' with a non-trivial destructor
您的代码中的问题之一是联合。
只有当它们的成员对它们有微不足道的暗示时,联合才会隐含地定义它们的特殊成员函数。
-> 将成员添加到实现特殊成员 function 的联合后,您必须为您的联合手动实现该成员。
来自 c++ 标准:
最多一个联合的非静态数据成员可以有一个大括号或等号初始化器。 [注意:如果联合的任何非静态数据成员具有非平凡的默认构造函数 (12.1)、复制构造函数 (12.8)、移动构造函数 (12.8)、复制赋值运算符 (12.8)、移动赋值运算符 (12.8),或析构函数(12.4),联合的对应成员 function 必须是用户提供的,否则它将为联合隐式删除(8.4.3)。 ——尾注]
例子:
// only trivial members: the compiler will provide all functions for you
union A {
int i;
float f;
double d;
// member functions provided by your compiler
A() = default;
A(const A&) = default;
A(A&&) = default;
A& operator=(const A&) = default;
A& operator=(A&&) = default;
~A() = default;
};
// std::string & std::vector have non-trivial constructors, assignment operators and destructors
// so the member functions will be deleted.
union B {
int i;
std::string s;
std::vector<int> v;
// member functions provided by your compiler
B() = delete;
B(const B&) = delete;
B(B&&) = delete;
B& operator=(const B&) = delete;
B& operator=(B&&) = delete;
~B() = delete;
};
这背后的原因是编译器实际上无法生成这些函数。 例如, union B
的构造函数需要知道它是否需要调用std::string
、 std::vector
的构造函数或根本不需要调用 - 但这取决于您初始化的编译器还不知道的成员。
union B
的析构函数也是如此——它应该调用哪个析构函数? std::string::~string()
? 或std::vector<int>::~vector()
? 或者在 int 情况下根本没有? 工会不知道哪个成员是活跃的,所以没有办法提供一个合理的析构函数。
因此,编译器将实际实现所有这些特殊功能的责任留给您。
实现两种类型的泛型联合的一个非常简单的示例如下所示:
template<class T, class U>
struct variant {
private:
enum {
IS_NONE,
IS_T,
IS_U
} activeMember;
union {
T t;
U u;
char dummy;
};
public:
variant() : activeMember(IS_NONE), dummy() {}
variant(T const& _t): activeMember(IS_T), t(_t) {}
variant(U const& _u): activeMember(IS_U), u(_u) {}
variant(variant const& other): variant() { *this = other; }
variant& operator=(variant const& other) {
if(&other != this) {
reset();
activeMember = other.activeMember;
if(other.activeMember == IS_T)
new (&t) T(other.t);
else if(other.activeMember == IS_U)
new (&u) U(other.u);
}
return *this;
}
variant& operator=(T const& _t) {
reset();
activeMember = IS_T;
new (&t) T(_t);
return *this;
}
variant& operator=(U const& _u) {
reset();
activeMember = IS_U;
new (&u) U(_u);
return *this;
}
explicit operator T const&() {
if(activeMember != IS_T) throw std::domain_error("variant is not T!");
return t;
}
explicit operator U const&() {
if(activeMember != IS_U) throw std::domain_error("variant is not U!");
return u;
}
explicit operator bool() {
return activeMember != IS_NONE;
}
~variant() { reset(); }
void reset() {
if(activeMember == IS_T)
t.~T();
else if(activeMember == IS_U)
u.~U();
activeMember = IS_NONE;
}
};
可以这样使用:
variant<int, std::string> v = 12;
v = "MIAU";
请注意,我们需要在构造函数中初始化联合成员之一,并根据需要调用它们各自的析构函数。
如果我们想在构造后切换活跃的成员,我们还需要先破坏之前活跃的联合成员,然后使用placement-new构造新的值。
std::variant
如果您可以使用 C++17,我建议您使用std::variant
而不是联合,因为这与我在上面发布的variant
class 基本上做同样的事情。
你使用memcpy( &m_Data._Ttype, &_Ttype, sizeof(T) );
在 class 的构造函数和赋值运算符中。
如果对象是可 简单复制的,则仅允许使用memcpy
复制对象,即:
static_assert(std::is_trivially_copyable<T>::value, "T is not trivially copyable!");
鉴于您得到的编译错误,您的T
很可能无法轻松复制,因此在这种情况下使用memcpy
会导致未定义的行为。
您可以改为使用placement new 来调用T
的复制或移动构造函数,例如:
template<class T>
Clustering<T>& Clustering<T>::operator=(T const& t)
{
new (&m_Data._Ttype) T(t);
return *this;
};
也不要忘记在你的~Clustering()
析构函数中为你的T
调用析构函数(如果它不是很容易破坏的话)(否则你会泄漏你的T
:):
template<class T>
Clustering<T>::~Clustering() {
if(/* union actually contains T */)
m_Data._Ttype.~T();
}
在您的示例中,您可以像这样编写Clustering
:
template <typename T>
class Clustering
{
friend CPlayer;
public:
union
{
T _Ttype;
CPlayer* m_player;
};
bool containsT;
public:
Clustering() : containsT(false), m_player(nullptr) {};
Clustering(T const& t) : containsT(true), _Ttype(t) {};
Clustering& operator=(T const& t)
{
// cleanup old T if needed
if(containsT)
_Ttype.~T();
containsT = true;
new (&_Ttype) T(t);
return *this;
};
~Clustering() {
if(containsT)
_Ttype.~T();
};
};
(请注意,我们可以避免清理m_player
,因为指针是微不足道的。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.