読者です 読者をやめる 読者になる 読者になる

union の const メンバ

ここに何の変哲もない union があったとする。

union point {
	struct {
		short x;
		short y;
	};
	int hash;
};

int のサイズとかは今回は置いといて、これを immutable にしたい。const 付ける。

union point {
	struct {
		const short x;
		const short y;
	};
	const int hash;
};
point p;
point.cpp:8:7: error: structure ‘p’ with uninitialized const members

まあ当然ですな。コンストラクタ付ける。

union point {
	struct {
		const short x;
		const short y;
	};
	const int hash;
	point(int x, int y) : x(x), y(y) {}
};
point p(0, 0);

gcc 4.6.2 でコンパイルすると問題なし。よかったよかった。
が、gcc 4.5.3 でコンパイルすると…

point.cpp: In constructor ‘point::point(int, int)’:
point.cpp:7:2: error: uninitialized member ‘point::hash’ with ‘const’ type ‘const int’

hash が初期化されてない…だと…。
4.6.2 では問題ない辺りを考えると、コンパイラのバグかな? まあバグにせよ動いて貰わんと困るわけだ。

union point {
	const int hash;
	struct {
		const short x;
		const short y;
	};
	point(int x, int y) : hash(0), x(x), y(y) {}
};
point p(0, 0);

うむ。4.5.3 では問題なくなった。4.6.2 では…

point.cpp: コンストラクタ ‘point::point(int, int)’ 内:
point.cpp:7:2: エラー: initializations for multiple members of ‘point’

おお、メッセージが一部ローカライズされてる!じゃなくて。
メンバが複数回初期化されている、と…まあおっしゃる通りなわけですが…。


うーん、となると、gcc 4.5 と 4.6 両方で通る immutable な union って作れないのだろうか。

試行錯誤1

試しにこうしてみた。

struct point {
	const union {
		int hash;
		struct {
			short x;
			short y;
		};
	};
	point(int x, int y) : x(x), y(y) {}
};

一応どちらでもコンパイルは通った。が、const にはならず…。
まあ const がメンバじゃなくて型にかかってるしね。むしろよくコンパイル通ったね。

試行錯誤2

カプセル化とか。

struct point {
	point(int x, int y) : x_(x), y_(y) {}
	int x() const { return x_; }
	int y() const { return y_; }
	int hash() const { return hash_; }

private:
	union {
		int hash_;
		struct {
			short x_;
			short y_;
		};
	};
};

…普通すぎてつまらん。
が、これくらいしか妥当な解決策思い付かない…。

おまけ: clang の場合

clang 3.0 でも色々試してみたのだけど、

union point {
	const int hash;
	struct {
		const short x;
		const short y;
	};
	point(int x, int y) : hash(0), x(x), y(y) {}
};
point p(0, 0);

通る。

struct point {
	union {
		const int hash;
		struct {
			const short x;
			const short y;
		};
	};
	point(int x, int y) : hash(0), x(x), y(y) {}
};
point p(0, 0);
point.cpp:9:33: error: initializing multiple members of anonymous union
        point(int x, int y) : hash(0), x(x), y(y) {}
                                       ^~~~
point.cpp:9:24: note: previous initialization is here
        point(int x, int y) : hash(0), x(x), y(y) {}
                              ^~~~~~~
point.cpp:9:39: error: initializing multiple members of anonymous union
        point(int x, int y) : hash(0), x(x), y(y) {}
                                             ^~~~
point.cpp:9:24: note: previous initialization is here
        point(int x, int y) : hash(0), x(x), y(y) {}
                              ^~~~~~~
2 errors generated.

エラー見やすい!じゃなくて。
こっちはダメなんか。違いがようわからん…。


C++ の仕様的にはこの辺りどうなってるんですかね。詳しい方教えてもらえると嬉しいです!



追記

コード一部改変して転載

union point {
        struct {
                const short x;
                const short y;
        };
        const int hash;
};
point p = {1, 2};

なるほど!union も {} で初期化できるんですね。知らなかったにしてもちょっと考えれば思い付きそうなものなだけに悔しい。
しかし、軽く試した感じだと最初のメンバに対してしか初期化できない感じ?
任意のメンバで初期化したい場合は結局コンストラクタが必要になって同じ問題が起きそうですね、むむむ…。



追記
さらに情報頂いたので載せておきます。

勉強になります。