C ++好友模板功能 - minGW错误但不是VS2015

时间:2016-05-02 02:19:40

标签: c++ templates friend-function template-function

以下代码编译时Visual Studio 2015没有问题,但minGW会收到下面显示的警告和错误:

#include <iostream>
using std::ostream;

template<typename ElemType, int SIZE>
class Array
{
    friend ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value);

    ElemType operator[](int index) const;

private:
    ElemType elements[SIZE];
};

template<typename ElemType, int SIZE>
ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value);

template<typename ElemType, int SIZE>
ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value)
{
    out << elements[0];
    return out;
}

mingw32-g++.exe -Wall -g -pedantic-errors -pedantic -Wextra -Wall -std=c++98 -c Test.cpp
Test.cpp:7:79: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const Array<ElemType, SIZE>&)' declares a non-template function [-Wnon-template-friend]
Test.cpp:7:79: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
Test.cpp: In function 'std::ostream& operator<<(std::ostream&, const Array<ElemType, SIZE>&)':
Test.cpp:21:11: error: 'elements' was not declared in this scope

我对某些东西的专家不太了解,所以我不确定问题是什么。它似乎告诉我它需要在类本身的友元声明之前的下面的代码,但是当我把它放在那里它会导致其他编译错误:

template<typename ElemType, int SIZE>

提前致谢!

在@Trevor Hickey在他的帖子中提出的更改之后,关于朋友模板功能的警告消失了。但是,我仍然得到关于&#34;元素&#34;的错误。 (在友元函数中)未在范围内声明。

2 个答案:

答案 0 :(得分:1)

您正在使用类中的模板参数,但函数本身不是模板函数。它必须是模板函数,如定义中所示。

#include <iostream>
using std::ostream;

template<typename ElemType, int SIZE>
class Array
{
    template<typename T, int U>
    friend ostream &operator<<(ostream &out, const Array<T, U> &value);

    ElemType operator[](int index) const;

private:
    ElemType elements[SIZE];
};

template<typename ElemType, int SIZE>
ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value)
{
    out << value.elements[0];
    return out;
}

答案 1 :(得分:1)

您的代码中存在两个单独的问题。更简单的是out << elements[0];应为out << value.elements[0];。这是因为您要打印作为参数value成员的元素。请记住,我们在此处处于非成员函数中,没有this,也没有可以通过非限定名称访问的成员。

另一个被称为template friends problem。到目前为止,您只收到警告,但如果您尝试编译完整的程序,则会出现错误。我添加了代码:

int main() { Array<int, 5> a; std::cout << a; }

并出现错误:

undefined reference to `std::ostream& operator<< <int, 5>(std::ostream&, Array<int, 5> const&)'

问题在于您的代码:

friend ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value);

实际上声明有一个非模板函数,它将成为此Array实例的朋友。稍后,当您在cout << a中编写main时,编译器会将<<与此非模板声明匹配。它永远不会像你后来提供的operator<<的主体那样,所以永远不会实例化该主体的副本,因此未定义的引用错误。

解决此问题的一种方法是显式实例化主体。但这很蹩脚,因为您必须为代码发生的Array的每个可能的实例化编写一个显式实例化。所以我们不会这样做。

最简单的解决方案是将operator<<的主体内联到类定义中。

另一种选择是声明operator<<是模板函数。

Trevor Hickey的代码显示了这样做的一种方法,尽管有一个缺点是Array<A, B>::elements可以访问Array<C, D>::operator<<

更安全的方法是在课前声明operator<<

template<typename ElemType, int SIZE>
class Array;

template<typename ElemType, int SIZE>
ostream &operator<<(ostream &out, const Array<ElemType, SIZE> &value);

然后是你以前的其余代码。现在,类中的friend声明将与预先存在的模板匹配,而不是声明新的非模板。