While working on my basic vector library, I've been trying to use a nice syntax for swizzle-based printing. The problem occurs when attempting to print a swizzle of a different dimension than the vector in question. In GCC 4.0, I originally had the friend << overloaded functions (with a body, even though it duplicated code) for every dimension in each vector, which caused the code to work, even if the non-native dimension code never actually was called. This failed in GCC 4.2. I recently realized (silly me) that only the function declaration was needed, not the body of the code, so I did that. Now I get the same warning on both GCC 4.0 and 4.2:
LINE 50 warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VECTOR3<TYPE>&)' declares a non-template function
Plus the five identical warnings more fo开发者_高级运维r the other function declarations.
The below example code shows off exactly what's going on and has all code necessary to reproduce the problem.
#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;
}
The code WORKS and produces the correct output, but I prefer warning free code whenever possible. I followed the advice the compiler gave me (summarized here and described by forums and StackOverflow as the answer to this warning) and added the two things that supposedly tells the compiler what's going on. That is, I added the function definitions as non-friends after the predefinitions of the templated unions:
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);
And, to each friend function that causes the issue, I added the <>
after the function name, such as for VECTOR2's case:
friend ostream& operator<< <> (ostream& os, const VECTOR3<TYPE>& toString);
friend ostream& operator<< <> (ostream& os, const VECTOR4<TYPE>& toString);
However, doing so leads to errors, such as:
LINE 139: error: no match for 'operator<<' in 'std::cout << my2dVector.VECTOR2<float>::xxx'
What's going on? Is it something related to how these templated union class-like structures are interrelated, or is it due to the unions themselves?
Update
After rethinking the issues involved and listening to the various suggestions of Potatoswatter, I found the final solution. Unlike just about every single cout overload example on the internet, I don't need access to the private member information, but can use the public interface to do what I wish. So, I make a non-friend overload functions that are inline for the swizzle parts that call the real friend overload functions. This bypasses the issues the compiler has with templated friend functions. I've added to the latest version of my project. It now works on both versions of GCC I tried with no warnings. The code in question looks like this:
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;
}
You can't declare a free friend
solely inside a class template. The definition is not visible outside the class until it's explicitly declared at global scope. (Edit: this code is an exception; the friends are found in main
by ADL. However, I don't understand why they aren't found by ADL in the other templates…) So, for example, after the closing template brace of VECTOR2
, add the prototype
template< class TYPE >
ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString);
Aside from that, the friend
s you presently have as bare prototypes look unnecessary to me. And s1
to s4
seem obviously redundant. The intent of the whole thing I can't discern.
CORRECTION
In this case, the free friend
cannot be a template because of the way implicit cast operator
s are used. So the above declaration won't match the non-template declaration within the VECTOR
template. Generation of non-template functions by a template via the friend mechanism is a strange and tricky language feature, which is being used correctly here with very little wiggle room. The best option is to disable the warning rather than attempt to refactor the sophisticated overload resolution.
UPDATE
Actually, there is a simple refactoring here. Just add operator<<
to the swizzle classes along with the implicit cast operator. (Sorry for the sloppy formatting.)
#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< class TYPE2 >
ostream& operator<<(ostream& os, const VECTOR2<TYPE2>& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ")";
return os;
}
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); }
friend ostream& operator<<( ostream &os, XX const &v ) { os << VECTOR2<TYPE>( v ); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); }
friend ostream& operator<<( ostream &os, XXX const &v ) { os << VECTOR3<TYPE>( v ); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); }
friend ostream& operator<<( ostream &os, XXXX const &v ) { os << VECTOR4<TYPE>( v ); } };
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);
};
template< class TYPE >
ostream& operator<<(ostream& os, const VECTOR3<TYPE>& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ", " << toString.v.z << ")";
return os;
}
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); }
friend ostream& operator<<( ostream &os, XX const &v ) { os << VECTOR2<TYPE>( v ); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); }
friend ostream& operator<<( ostream &os, XXX const &v ) { os << VECTOR3<TYPE>( v ); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); }
friend ostream& operator<<( ostream &os, XXXX const &v ) { os << VECTOR4<TYPE>( v ); } };
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);
};
template< class TYPE >
ostream& operator<<(ostream& os, const VECTOR4<TYPE>& toString)
{
os << "(" << toString.v.x << ", " << toString.v.y << ", " << toString.v.z << ", " << toString.v.w << ")";
return os;
}
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); }
friend ostream& operator<<( ostream &os, XX const &v ) { os << VECTOR2<TYPE>( v ); } };
struct XXX : s3 { operator VECTOR3<TYPE>() const { return VECTOR3<TYPE>(s3::x, s3::x, s3::x); }
friend ostream& operator<<( ostream &os, XXX const &v ) { os << VECTOR3<TYPE>( v ); } };
struct XXXX : s4 { operator VECTOR4<TYPE>() const { return VECTOR4<TYPE>(s4::x, s4::x, s4::x, s4::x); }
friend ostream& operator<<( ostream &os, XXXX const &v ) { os << VECTOR4<TYPE>( v ); } };
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);
};
// 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;
}
Quite strangely, the functions I added are still non-template friends defined within a template, quite similar all-around to the functions I eliminated. But GCC doesn't complain about them for whatever reason. If it did, you could make them templates and move them outside to proper namespace scope, anyway.
By the way, I don't personally prefer this code over the original. If I were you, I'd just disable the warning.
The faq here http://gcc.gnu.org/faq.html gives more detail under the section "Miscellaneous"
精彩评论