我對Clustering班級有問題,其中的工會造成了問題。
我已經嘗試了幾件事,但仍然沒有,檢查了運營商,但無法找到錯誤。
它最初是用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類的定義:
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
- 完整代碼:https : //shrib.com/#Woodrat5GwaRRM
- 完整構建輸出:https : //shrib.com/#Yaretzi3mo9xQ4
uj5u.com熱心網友回復:
1. 非平凡的工會成員
代碼中的問題之一是聯合。
聯合僅在其成員對它們具有微不足道的實作時才隱式定義其特殊成員函式。
-> 將成員添加到實作特殊成員函式的聯合后,您必須為聯合手動實作該成員。
從 C 標準:
聯合的最多一個非靜態資料成員可以有一個大括號或等號初始化器。[注意:如果聯合的任何非靜態資料成員具有非平凡的默認建構式 (12.1)、復制建構式 (12.8)、移動建構式 (12.8)、復制賦值運算子 (12.8)、移動賦值運算子 (12.8),或解構式(12.4),聯合體對應的成員函式必須是用戶提供的,否則會為聯合體隱式洗掉(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;
};
Godbolt 示例與您的代碼
這背后的原因是編譯器無法實際生成這些函式。例如, 的建構式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;
}
};
Godbolt 示例
可以這樣使用:
variant<int, std::string> v = 12;
v = "MIAU";
Notice that we need to initialize one of the union members in the constructors, as well as call their respective destructors as needed.
If we want to switch the active member after construction we also need to destruct the previosly active union member first, then use placement-new to construct the new value.
1.1 using std::variant
If you can use C 17 i'd recommend using std::variant instead of the union, since that basically does the exact same thing as the variant class i posted above and more.
2. memcpy of non-trival types
You use memcpy( &m_Data._Ttype, &_Ttype, sizeof(T) ); both in the constructor and assignment operator of your class.
You are only allowed to copy objects with memcpy if they are trivially copyable, i.e.:
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;
};
也不要忘記在你的解構式中為你的T(如果它不是微不足道的可破壞的)呼叫析構~Clustering()函式(否則你會泄漏你的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,因為指標是微不足道的。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/376885.html
