union
Note
C++17 以降では、std::variant
class が union のタイプ セーフな代替手段です。
union
とは、すべてのメンバーが同じメモリの場所を共有するユーザー定義の型です。 この定義は、任意のどの時点においても、union はそのメンバー一覧にあるオブジェクトを 1 つだけ含むことができることを意味します。 また、union のメンバー数には関係なく、最大のメンバーを格納するのに必要なメモリだけが常に使用されることも意味します。
union は、多数のオブジェクトやメモリの制限がある場合、メモリの節約に役立ちます。 ただし、union を正しく使用するには特別な注意が必要です。 割り当てたのと同じメンバーに常にアクセスする必要があります。 いずれかのメンバー型に非トリガー constructがある場合、またはそのメンバーを明示的に結合struct して破棄するコードを記述する必要があります。 union を使用する前に、解決すべき問題が、基本データ型 class および派生 class 型を使用することでより適切に表現できないか検討します。
構文
union
tag
opt{
member-list
};
パラメーター
tag
union に渡す型名。
member-list
union に含めることができるメンバー。
union を宣言する
union の宣言は union
キーワードを使用して開始し、メンバー リストを中かっこで囲みます。
// declaring_a_union.cpp
union RecordType // Declare a simple union type
{
char ch;
int i;
long l;
float f;
double d;
int *int_ptr;
};
int main()
{
RecordType t;
t.i = 5; // t holds an int
t.f = 7.25; // t now holds a float
}
union を使用する
前の例では、union にアクセスするすべてのコードが、データを保持しているメンバーを知る必要がありました。 この問題の最も一般的な解決策は、"判別共用体" と呼ばれます。 union を struct で囲み、union に現在格納されているメンバー型を示す enum メンバーを含めます。 次の例に、基本的なパターンを示します。
#include <queue>
using namespace std;
enum class WeatherDataType
{
Temperature, Wind
};
struct TempData
{
int StationId;
time_t time;
double current;
double max;
double min;
};
struct WindData
{
int StationId;
time_t time;
int speed;
short direction;
};
struct Input
{
WeatherDataType type;
union
{
TempData temp;
WindData wind;
};
};
// Functions that are specific to data types
void Process_Temp(TempData t) {}
void Process_Wind(WindData w) {}
void Initialize(std::queue<Input>& inputs)
{
Input first;
first.type = WeatherDataType::Temperature;
first.temp = { 101, 1418855664, 91.8, 108.5, 67.2 };
inputs.push(first);
Input second;
second.type = WeatherDataType::Wind;
second.wind = { 204, 1418859354, 14, 27 };
inputs.push(second);
}
int main(int argc, char* argv[])
{
// Container for all the data records
queue<Input> inputs;
Initialize(inputs);
while (!inputs.empty())
{
Input const i = inputs.front();
switch (i.type)
{
case WeatherDataType::Temperature:
Process_Temp(i.temp);
break;
case WeatherDataType::Wind:
Process_Wind(i.wind);
break;
default:
break;
}
inputs.pop();
}
return 0;
}
前の例で、Input
struct の union には名前がないので、"匿名" union と呼ばれます。 そのメンバーには、struct のメンバーである場合と同様に直接アクセスできます。 匿名 union の使用方法の詳細については、「匿名 union」セクションを参照してください。
前の例は、共通の基本 class から派生した class 型を使用して解決できる問題を示しています。 コンテナー内の各オブジェクトのランタイム型に基づいてコードを分岐させることができます。 コードの維持や理解は容易になりますが、union を使用するより低速になる場合があります。 また、union を使用すると、関連のない型を格納できます。 union を使用すると、union 変数自体の型を変更せずに、格納されている値の型を動的に変更できます。 たとえば、要素にさまざまな型のさまざまな値を格納する MyUnionType
の異種配列を作成できます。
この例では、Input
struct の誤用が簡単に発生します。 データを保持するメンバーにアクセスするには、ユーザーが識別子を正しく使用する必要があります。 次の例で示すように、union を private
にし、特別なアクセス関数を指定することで、誤用を防ぐことができます。
制限されていない union (C++11)
C++03 以前では、union型にユーザーが指定した class constructors、destructors、または代入演算子がない限り、a には型を持つ非staticデータ メンバーを含めることができます。 C++11 では、これらの制限が削除されています。 そのようなメンバーを union に含めた場合、ユーザー指定ではない特殊なメンバー関数は、コンパイラによって自動的に deleted
としてマークされます。 union が class または struct の内部にある匿名 union の場合、ユーザー指定ではない class または struct の特殊なメンバー関数は deleted
としてマークされます。 このケースを処理する方法を次の例に示します。 union のメンバーの 1 つには、この特別な処理を必要とするメンバーがあります。
// for MyVariant
#include <crtdbg.h>
#include <new>
#include <utility>
// for sample objects and output
#include <string>
#include <vector>
#include <iostream>
using namespace std;
struct A
{
A() = default;
A(int i, const string& str) : num(i), name(str) {}
int num;
string name;
//...
};
struct B
{
B() = default;
B(int i, const string& str) : num(i), name(str) {}
int num;
string name;
vector<int> vec;
// ...
};
enum class Kind { None, A, B, Integer };
#pragma warning (push)
#pragma warning(disable:4624)
class MyVariant
{
public:
MyVariant()
: kind_(Kind::None)
{
}
MyVariant(Kind kind)
: kind_(kind)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A();
break;
case Kind::B:
new (&b_) B();
break;
case Kind::Integer:
i_ = 0;
break;
default:
_ASSERT(false);
break;
}
}
~MyVariant()
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
a_.~A();
break;
case Kind::B:
b_.~B();
break;
case Kind::Integer:
break;
default:
_ASSERT(false);
break;
}
kind_ = Kind::None;
}
MyVariant(const MyVariant& other)
: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(other.a_);
break;
case Kind::B:
new (&b_) B(other.b_);
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
}
MyVariant(MyVariant&& other)
: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(move(other.a_));
break;
case Kind::B:
new (&b_) B(move(other.b_));
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
}
MyVariant& operator=(const MyVariant& other)
{
if (&other != this)
{
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = other.a_;
break;
case Kind::B:
*this = other.b_;
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
}
return *this;
}
MyVariant& operator=(MyVariant&& other)
{
_ASSERT(this != &other);
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = move(other.a_);
break;
case Kind::B:
*this = move(other.b_);
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
return *this;
}
MyVariant(const A& a)
: kind_(Kind::A), a_(a)
{
}
MyVariant(A&& a)
: kind_(Kind::A), a_(move(a))
{
}
MyVariant& operator=(const A& a)
{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(a);
}
else
{
a_ = a;
}
return *this;
}
MyVariant& operator=(A&& a)
{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(move(a));
}
else
{
a_ = move(a);
}
return *this;
}
MyVariant(const B& b)
: kind_(Kind::B), b_(b)
{
}
MyVariant(B&& b)
: kind_(Kind::B), b_(move(b))
{
}
MyVariant& operator=(const B& b)
{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(b);
}
else
{
b_ = b;
}
return *this;
}
MyVariant& operator=(B&& b)
{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(move(b));
}
else
{
b_ = move(b);
}
return *this;
}
MyVariant(int i)
: kind_(Kind::Integer), i_(i)
{
}
MyVariant& operator=(int i)
{
if (kind_ != Kind::Integer)
{
this->~MyVariant();
new (this) MyVariant(i);
}
else
{
i_ = i;
}
return *this;
}
Kind GetKind() const
{
return kind_;
}
A& GetA()
{
_ASSERT(kind_ == Kind::A);
return a_;
}
const A& GetA() const
{
_ASSERT(kind_ == Kind::A);
return a_;
}
B& GetB()
{
_ASSERT(kind_ == Kind::B);
return b_;
}
const B& GetB() const
{
_ASSERT(kind_ == Kind::B);
return b_;
}
int& GetInteger()
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}
const int& GetInteger() const
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}
private:
Kind kind_;
union
{
A a_;
B b_;
int i_;
};
};
#pragma warning (pop)
int main()
{
A a(1, "Hello from A");
B b(2, "Hello from B");
MyVariant mv_1 = a;
cout << "mv_1 = a: " << mv_1.GetA().name << endl;
mv_1 = b;
cout << "mv_1 = b: " << mv_1.GetB().name << endl;
mv_1 = A(3, "hello again from A");
cout << R"aaa(mv_1 = A(3, "hello again from A"): )aaa" << mv_1.GetA().name << endl;
mv_1 = 42;
cout << "mv_1 = 42: " << mv_1.GetInteger() << endl;
b.vec = { 10,20,30,40,50 };
mv_1 = move(b);
cout << "After move, mv_1 = b: vec.size = " << mv_1.GetB().vec.size() << endl;
cout << endl << "Press a letter" << endl;
char c;
cin >> c;
}
union は、参照を格納できません。 また、A union は継承をサポートしていません。 つまり、union を基底 class として使用したり、別の class から継承したり、仮想関数を使用したりすることはできません。
を初期化します。 union
中かっこで囲まれた式を代入して、同じステートメントで union の宣言と初期化を実行できます。 この式は評価され、union の最初のフィールドに割り当てられます。
#include <iostream>
using namespace std;
union NumericType
{
short iValue;
long lValue;
double dValue;
};
int main()
{
union NumericType Values = { 10 }; // iValue = 10
cout << Values.iValue << endl;
Values.dValue = 3.1416;
cout << Values.dValue << endl;
}
/* Output:
10
3.141600
*/
次の NumericType
union 図に示すように、(概念的には) メモリ内に配置されます。
この図は、8 バイトのデータを示しています。 double 型 dValue は、8 バイト全体を占有します。 型 long lValue は最初の 4 バイトを占有します。 short 型 iValue は最初のバイトを占有します。
匿名 union
匿名 union は、class-name
または declarator-list
を使用せずに宣言されたものです。
union {
member-list
}
匿名 union 共用体で宣言されている名前は非メンバー変数のように直接使用されます。 これは、匿名 union で宣言された名前は前後のスコープで一意である必要があることを意味します。
匿名 union ユーザーには、次の制限が適用されます。
- ファイル スコープか名前空間スコープで宣言されている場合、
static
としても宣言する必要があります。 public
メンバーのみを含むことができます。匿名 union にprivate
メンバーやprotected
メンバーが含まれると、エラーが生成されます。- メンバー関数を持つことはできません。
関連項目
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示