Templated << друг не работает, когда находится во взаимосвязи с другими типами шаблонных объединений - PullRequest
1 голос
/ 16 августа 2010

Работая над моей базовой векторной библиотекой , я пытался использовать хороший синтаксис для печати на основе мошенничества. Проблема возникает при попытке напечатать изобилие другого размера, чем рассматриваемый вектор. В 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;
}

Ответы [ 2 ]

2 голосов
/ 16 августа 2010

Вы не можете объявить свободный friend только внутри шаблона класса.Определение не отображается за пределами класса, пока оно не будет явно объявлено в глобальной области видимости.(Изменить: этот код является исключением; друзья находят в main по ADL. Однако я не понимаю, почему они не найдены по ADL в других шаблонах…) Так, например, после закрывающего шаблонаскобка VECTOR2, добавьте прототип

template< class TYPE >
ostream& operator<<(ostream& os, const VECTOR2<TYPE>& toString);

Помимо этого, friend s, которые у вас сейчас есть, поскольку голые прототипы кажутся мне ненужнымиИ s1 до s4 кажутся явно избыточными.Намерение всего этого я не могу различить.

КОРРЕКЦИЯ

В этом случае свободный friend не может быть шаблоном из-за способа использования неявного приведения operator s.,Поэтому приведенное выше объявление не будет соответствовать объявлению, не являющемуся шаблоном в шаблоне VECTOR.Генерация не шаблонных функций с помощью шаблона через механизм Friend - странная и хитрая языковая функция, которая используется здесь правильно с очень небольшим пространством для маневра.Лучшим вариантом является отключение предупреждения, а не попытка рефакторинга сложного разрешения перегрузки.

ОБНОВЛЕНИЕ

На самом деле, здесь есть простой рефакторинг.Просто добавьте operator<< к классам swizzle вместе с неявным оператором приведения.(Извините за небрежное форматирование.)

#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;
}

Как ни странно, функции, которые я добавил , являются , все еще не являющимися шаблонными друзьями, определенными в шаблоне, очень похожими на все функции, которые яустранены.Но GCC не жалуется на них по любой причине.Если бы это было так, вы могли бы сделать их шаблонами и переместить их за пределы области видимости, в любом случае.

Кстати, я лично не предпочитаю этот код оригиналу.На твоем месте я бы просто отключил предупреждение.

1 голос
/ 16 августа 2010

FAQ здесь http://gcc.gnu.org/faq.html дает более подробную информацию в разделе "Разное"

...