在处理我的基本向量库时,我一直在尝试使用一种很好的语法来进行基于 swizzle 的打印。尝试打印与所讨论的向量不同维度的调酒时会出现问题。在 GCC 4.0 中,我最初为每个向量中的每个维度使用了朋友 << 重载函数(带有一个主体,即使它重复了代码),这导致代码工作,即使实际上从未调用过非本机维度代码. 这在 GCC 4.2 中失败了。我最近意识到(愚蠢的我)只需要函数声明,而不是代码主体,所以我这样做了。现在我在 GCC 4.0 和 4.2 上都得到了同样的警告:
LINE 50 warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VECTOR3<TYPE>&)' declares a non-template function
加上其他函数声明的五个相同警告。
下面的示例代码准确地展示了正在发生的事情,并包含重现问题所需的所有代码。
#include <iostream> // cout, endl
#include <sstream> // ostream, ostringstream, string
using std::cout;
using std::endl;
using std::string;
using std::ostream;
// Predefines
template <typename TYPE> union VECTOR2;
template <typename TYPE> union VECTOR3;
template <typename TYPE> union VECTOR4;
typedef VECTOR2<float> vec2;
typedef VECTOR3<float> vec3;
typedef VECTOR4<float> vec4;
template <typename TYPE>
union VECTOR2
{
private:
struct { TYPE x, y; } v;
struct s1 { protected: TYPE x, y; };
struct s2 { protected: TYPE x, y; };
struct s3 { protected: TYPE x, y; };
struct s4 { protected: TYPE x, y; };
struct X : s1 { operator TYPE() const { return s1::x; } };
struct XX : s2 { operator VECTOR2<TYPE>() const { return VECTOR2<TYPE>(s2::x, s2::x); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); } };
public:
VECTOR2() {}
VECTOR2(const TYPE& x, const TYPE& y) { v.x = x; v.y = y; }
X x;
XX xx;
XXX xxx;
XXXX xxxx;
// Overload for cout
friend ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ")";
return os;
}
friend ostream& operator<<(ostream& os, const VECTOR3<TYPE>& toString);
friend ostream& operator<<(ostream& os, const VECTOR4<TYPE>& toString);
};
template <typename TYPE>
union VECTOR3
{
private:
struct { TYPE x, y, z; } v;
struct s1 { protected: TYPE x, y, z; };
struct s2 { protected: TYPE x, y, z; };
struct s3 { protected: TYPE x, y, z; };
struct s4 { protected: TYPE x, y, z; };
struct X : s1 { operator TYPE() const { return s1::x; } };
struct XX : s2 { operator VECTOR2<TYPE>() const { return VECTOR2<TYPE>(s2::x, s2::x); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); } };
public:
VECTOR3() {}
VECTOR3(const TYPE& x, const TYPE& y, const TYPE& z) { v.x = x; v.y = y; v.z = z; }
X x;
XX xx;
XXX xxx;
XXXX xxxx;
// Overload for cout
friend ostream& operator<<(ostream& os, const VECTOR3<TYPE>& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ", " << toString.v.z << ")";
return os;
}
friend ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString);
friend ostream& operator<<(ostream& os, const VECTOR4<TYPE>& toString);
};
template <typename TYPE>
union VECTOR4
{
private:
struct { TYPE x, y, z, w; } v;
struct s1 { protected: TYPE x, y, z, w; };
struct s2 { protected: TYPE x, y, z, w; };
struct s3 { protected: TYPE x, y, z, w; };
struct s4 { protected: TYPE x, y, z, w; };
struct X : s1 { operator TYPE() const { return s1::x; } };
struct XX : s2 { operator VECTOR2<TYPE>() const { return VECTOR2<TYPE>(s2::x, s2::x); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); } };
public:
VECTOR4() {}
VECTOR4(const TYPE& x, const TYPE& y, const TYPE& z, const TYPE& w) { v.x = x; v.y = y; v.z = z; v.w = w; }
X x;
XX xx;
XXX xxx;
XXXX xxxx;
// Overload for cout
friend ostream& operator<<(ostream& os, const VECTOR4& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ", " << toString.v.z << ", " << toString.v.w << ")";
return os;
}
friend ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString);
friend ostream& operator<<(ostream& os, const VECTOR3<TYPE>& toString);
};
// Test code
int main (int argc, char * const argv[])
{
vec2 my2dVector(1, 2);
cout << my2dVector.x << endl;
cout << my2dVector.xx << endl;
cout << my2dVector.xxx << endl;
cout << my2dVector.xxxx << endl;
vec3 my3dVector(3, 4, 5);
cout << my3dVector.x << endl;
cout << my3dVector.xx << endl;
cout << my3dVector.xxx << endl;
cout << my3dVector.xxxx << endl;
vec4 my4dVector(6, 7, 8, 9);
cout << my4dVector.x << endl;
cout << my4dVector.xx << endl;
cout << my4dVector.xxx << endl;
cout << my4dVector.xxxx << endl;
return 0;
}
该代码工作并产生正确的输出,但我更喜欢尽可能无警告代码。我遵循了编译器给我的建议(在此处总结并由论坛和 StackOverflow 描述为该警告的答案),并添加了两件本应告诉编译器发生了什么的事情。也就是说,我在模板联合的预定义之后将函数定义添加为非朋友:
template <typename TYPE> ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString);
template <typename TYPE> ostream& operator<<(ostream& os, const VECTOR3<TYPE>& toString);
template <typename TYPE> ostream& operator<<(ostream& os, const VECTOR4<TYPE>& toString);
并且,对于导致问题的每个友元函数,我<>
在函数名称后添加了一个,例如 VECTOR2 的情况:
friend ostream& operator<< <> (ostream& os, const VECTOR3<TYPE>& toString);
friend ostream& operator<< <> (ostream& os, const VECTOR4<TYPE>& toString);
但是,这样做会导致错误,例如:
LINE 139: error: no match for 'operator<<' in 'std::cout << my2dVector.VECTOR2<float>::xxx'
这是怎么回事?是与这些模板化的类联合类结构如何相互关联有关,还是由于联合本身?
更新
在重新思考所涉及的问题,并听取了 Potatoswatter 的各种建议后,我找到了最终的解决方案。与互联网上几乎每个 cout 重载示例不同,我不需要访问私有成员信息,但可以使用公共接口来做我想做的事。因此,我为调用真正的友元重载函数的调酒部分创建了一个非友元重载函数。这绕过了编译器对模板化友元函数的问题。我已添加到我的项目的最新版本。它现在适用于我尝试过的两个 GCC 版本,没有任何警告。有问题的代码如下所示:
template <typename SWIZZLE> inline
typename EnableIf< Is2D< typename SWIZZLE::PARENT >, ostream >::type&
operator<<(ostream& os, const SWIZZLE& printVector)
{
os << (typename SWIZZLE::PARENT(printVector));
return os;
}
template <typename SWIZZLE> inline
typename EnableIf< Is3D< typename SWIZZLE::PARENT >, ostream >::type&
operator<<(ostream& os, const SWIZZLE& printVector)
{
os << (typename SWIZZLE::PARENT(printVector));
return os;
}
template <typename SWIZZLE> inline
typename EnableIf< Is4D< typename SWIZZLE::PARENT >, ostream >::type&
operator<<(ostream& os, const SWIZZLE& printVector)
{
os << (typename SWIZZLE::PARENT(printVector));
return os;
}