ページ 11

VC++で無名構造体、無名共用体を併用した時の挙動

Posted: 2015年10月17日(土) 13:39
by かがみ
VC++での無名構造体と無名共用体を併用した際に意図しない動作になってしまい、調べてみたんですが具体的な例がでてきませんでした。
試しにGCCでも試してみて、GCCのほうではちゃんとした動作をしましたがなぜ同じ動作にならないのかわかりませんでした。
GCCのほうは使い初めて間もないのでテストのみです。

意図しない挙動になったのはVec3です。Vec2はVCでもGCCでもちゃんとした動作になりました。
ご教授よろしくお願いします。

環境:
Visual Studio 2013, Win7
(GCC5.2.0)

コード:

/* Math.h */
/* 使用しているところのみ抜粋 */

#pragma once

#include <cmath>

template <typename T>
class Math
{
public:
	static const T Zero; /* 0.0 */
	static const T One; /* 1.0 */
};
template <> const int Math<int>::Zero = 0;
template <> const float Math<float>::Zero = 0.0f;
template <> const double Math<double>::Zero = 0.0;
template <> const long double Math<long double>::Zero = 0.0L;
template <> const int Math<int>::One = 1;
template <> const float Math<float>::One = 1.0f;
template <> const double Math<double>::One = 1.0;
template <> const long double Math<long double>::One = 1.0L;

typedef Math<int> Mathi;
typedef Math<float> Mathf;
typedef Math<double> Mathd;
typedef Math<long double> Mathdl;

コード:

/* Vec2.h */

#pragma once

namespace std
{
	template <typename, typename>
	class basic_ostream;
}

#include "Math.h"

template <typename T>
struct Vec2
{
	union
	{
		struct
		{
			T X;
			T Y;
		};
		T Data[2];
	};

	static const Vec2 Zero;
	static const Vec2 One;
	static const Vec2 UnitX;
	static const Vec2 UnitY;

	Vec2() :
		X(Math<T>::Zero), Y(Math<T>::Zero)
	{

	}
	Vec2(T x, T y) :
		X(x), Y(y)
	{

	}
};

template <typename T>
const Vec2<T> Vec2<T>::Zero(Math<T>::Zero, Math<T>::Zero);
template <typename T>
const Vec2<T> Vec2<T>::One(Math<T>::One, Math<T>::One);
template <typename T>
const Vec2<T> Vec2<T>::UnitX(Math<T>::One, Math<T>::Zero);
template <typename T>
const Vec2<T> Vec2<T>::UnitY(Math<T>::Zero, Math<T>::One);

template <typename CharT, typename Traits, typename T>
inline std::basic_ostream<CharT, Traits>& operator << (std::basic_ostream<CharT, Traits>& os, const Vec2<T>& v)
{
	os << v.X << ' ' << v.Y;
	return os;
}

コード:

/* Vec3.h */

#pragma once

#include "Vec2.h"

template <typename T>
struct Vec3
{
	union
	{
		struct
		{
			T X;
			T Y;
			T Z;
		};
		struct
		{
			Vec2<T> XY; //これがあるかないかでVCの挙動がおかしくなる
		};
		T Data[3];
	};

	static const Vec3 Zero;
	static const Vec3 One;
	static const Vec3 UnitX;
	static const Vec3 UnitY;
	static const Vec3 UnitZ;

	Vec3() :
		Vec3(Math<T>::Zero, Math<T>::Zero, Math<T>::Zero)
	{

	}
	Vec3(T x, T y, T z) :
		//XY(x, y), Z(z) //VCの場合はこちらを使用するとうまく動作、GCCではエラー
		X(x), Y(y), Z(z)
	{

	}
	Vec3(const Vec2<T>& v, T z) :
		Vec3(v.X, v.Y, z)
	{

	}
};

template <typename T>
const Vec3<T> Vec3<T>::Zero(Math<T>::Zero, Math<T>::Zero, Math<T>::Zero);
template <typename T>
const Vec3<T> Vec3<T>::One(Math<T>::One, Math<T>::One, Math<T>::One);
template <typename T>
const Vec3<T> Vec3<T>::UnitX(Math<T>::One, Math<T>::Zero, Math<T>::Zero);
template <typename T>
const Vec3<T> Vec3<T>::UnitY(Math<T>::Zero, Math<T>::One, Math<T>::Zero);
template <typename T>
const Vec3<T> Vec3<T>::UnitZ(Math<T>::Zero, Math<T>::Zero, Math<T>::One);

template <typename CharT, typename Traits, typename T>
inline std::basic_ostream<CharT, Traits>& operator << (std::basic_ostream<CharT, Traits>& os, const Vec3<T>& v)
{
	os << v.X << ' ' << v.Y << ' ' << v.Z;
	return os;
}

コード:

/* main.cpp */

#ifndef _MSC_VER
#pragma anon_unions
#else
#pragma warning(disable: 4201)
#endif

#include "Vec2.h"
#include "Vec3.h"
#include "Vec4.h"
#include <iostream>

using std::cout;
using std::endl;

typedef Vec2<float> Vec2f;
typedef Vec3<float> Vec3f;
typedef Vec4<float> Vec4f;

int main(int argc, char** argv)
{
	cout << std::fixed;

	const auto v2 = sizeof(Vec2f);
	const auto v3 = sizeof(Vec3f);
	const auto v4 = sizeof(Vec4f);

	cout << Vec2f::UnitX << endl;
	cout << Vec3f::UnitX << endl;
	cout << Vec3f::UnitY << endl;
	cout << Vec3f::UnitZ << endl;
	cout << Vec3f::UnitX.XY << endl;

	return 0;
}

Re: VC++で無名構造体、無名共用体を併用した時の挙動

Posted: 2015年10月17日(土) 13:46
by かがみ
実行結果を書いていませんでした。もうしわけありません。

1.000000 0.000000
0.000000 0.000000 0.000000
0.000000 0.000000 0.000000
0.000000 0.000000 1.000000
0.000000 0.000000

VCでの挙動です。

Re: VC++で無名構造体、無名共用体を併用した時の挙動

Posted: 2015年10月17日(土) 14:46
by (こ)
注意: これは回答ではありません。単なる情報提供です。

提示されたコードで使われているのは、無名構造体 "unnamed structs" ではなく、匿名構造体 "anonymous structs" です。
無名構造体は以下のような奴ですね。

コード:

struct { ... } foo;
typedef struct { ... } bar_t;
で、匿名構造体はC++標準に含まれていない、Microsoft の独自拡張です( https://msdn.microsoft.com/en-us/library/a3bbz53t.aspx に Microsoft Specific と書かれている辺り)。C11規格には入りましたが、C++11/C++14には入りませんでした。
gcc も独自拡張としてサポートしていますが(unnamed fields とか呼んでるっぽい)、いずれにせよ標準規格外である以上、両者の実装が同一であるとは限りません。注意しましょう。

参考: http://stackoverflow.com/q/14248044 http://stackoverflow.com/q/2253878

Re: VC++で無名構造体、無名共用体を併用した時の挙動

Posted: 2015年10月17日(土) 16:53
by かがみ
> (こ)さん、情報ありがとうございます。
C++標準ではないことや偶然使った両コンパイラが独自で実装していただけとは知りませんでした。
特にすべてのコンパイラに対応するつもりはありませんが、使用を控えていこうと思います。

解決とさせていただきます。ありがとうございました。

Re: VC++で無名構造体、無名共用体を併用した時の挙動

Posted: 2015年10月17日(土) 18:11
by hoge
(こ) さんが書きました: 提示されたコードで使われているのは、無名構造体 "unnamed structs" ではなく、匿名構造体 "anonymous structs" です。
無名構造体は以下のような奴ですね。
そんなことはないですよ。
少々古い規格ですが、少なくともJIS X 3014では "unnamed class" を名前なしクラス、 "anonymous union" を無名共用体と呼んでいます。
まさか、構造体と共用体の違いで "anonymous” の訳語が異なるとでも主張されるのでしょうか?