模板化<<朋友在与其他模板化的联合类型相互关联时不工作

时间:2010-08-15 22:55:54

标签: c++ templates overloading friend unions

在我的basic vector library上工作时,我一直在尝试使用一个很好的语法来进行基于混音的打印。当尝试打印与所讨论的矢量不同的维度的混合时,会出现问题。在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;
}

代码WORKS并生成正确的输出,但我更喜欢尽可能警告免费代码。我按照编译器给我的建议(summarized here并由论坛和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;
}

2 个答案:

答案 0 :(得分:2)

您不能仅在类模板中声明免费friend。除非在全局范围内明确声明,否则该定义在类外部不可见。 (编辑:此代码是一个例外; ADL在main中找到了朋友。但是,我不明白为什么ADL在其他模板中找不到它们...)所以,例如,在关闭VECTOR2的模板大括号,添加原型

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

除此之外,你现在拥有的friend裸机原型对我来说是不必要的。 s1s4似乎显然是多余的。整个事情的意图我无法辨别。

CORRECTION

在这种情况下,由于使用隐式转换friend的方式,免费operator不能是模板。因此,上述声明与VECTOR模板中的非模板声明不匹配。通过朋友机制通过模板生成非模板函数是一种奇怪且棘手的语言特征,这里正确使用了很少的摆动空间。最好的选择是禁用警告而不是尝试重构复杂的重载决策。

更新

实际上,这里有一个简单的重构。只需将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;
}

很奇怪,我添加的函数仍然是模板中定义的非模板朋友,与我消除的函数完全相似。但海湾合作委员会不会因任何原因抱怨他们。如果确实如此,那么无论如何,你可以制作模板并将它们移到合适的命名空间范围内。

顺便说一句,我个人不喜欢这个代码而不是原始代码。如果我是你,我只是禁用警告。

答案 1 :(得分:1)

此处的常见问题http://gcc.gnu.org/faq.html在“杂项”部分

下提供了更多详细信息