Работая над моей базовой векторной библиотекой , я пытался использовать хороший синтаксис для печати на основе мошенничества. Проблема возникает при попытке напечатать изобилие другого размера, чем рассматриваемый вектор. В GCC 4.0 у меня изначально были перегруженные функции друга << (с телом, даже если он дублировал код) для каждого измерения в каждом векторе, что заставляло код работать, даже если на самом деле код неродного измерения никогда не вызывался , Это не удалось в GCC 4.2. Недавно я понял (глупый я), что нужно только объявление функции, а не тело кода, поэтому я сделал это. Теперь я получаю одно и то же предупреждение на GCC 4.0 и 4.2: </p>
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 в Интернете, мне не нужен доступ к личной информации участника, но я могу использовать открытый интерфейс, чтобы делать то, что я хочу. Итак, я создаю функции перегрузки, не являющиеся друзьями, встроенные для частей Swizzle, которые вызывают функции перегрузки для настоящих друзей. Это позволяет обойти проблемы, возникающие у компилятора с шаблонными функциями-друзьями. Я добавил в последнюю версию моего проекта. Теперь он работает на обеих версиях 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;
}