c++templatesfriend-functiontemplate-function

C++ friend template function - minGW error but not VS2015


The following code compiles without a problem with Visual Studio 2015 but with minGW gets the warning and error shown below it:

#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

I'm far from an expert on some of this stuff so I'm not sure what the issue is. It seems like it's telling me it needs the follwoing code just before the friend declaration in the class itself, but when I put it there it causes other compilation errors:

template<typename ElemType, int SIZE>

Thanks in advance!

After making the changes suggested by @Trevor Hickey in his post below the warning about the friend template function went away. However, I still get the error about "elements" (in the friend function) not being declared in scope.


Solution

  • There's two separate problems in your code. The easier one is that out << elements[0]; should be out << value.elements[0];. This is because you want to print the element that is a member of the parameter value. Remember that we are in a non-member function here, there is no this and no members you can access by unqualified name.

    The other one is known as the template friends problem. So far you only get a warning, but if you try to compile a complete program you get an error. I added the code:

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

    and the error appears:

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

    The problem is that your code:

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

    actually declares that there is a non-template function which will be the friend to this instance of Array. Later, when you write cout << a in main, the compiler matches that << to this non-template declaration. It never gets so far as the body for operator<< that you provided later, and so never instantiates a copy of that body, hence the undefined reference error.

    One way to fix this is to explicitly instantiate the body. But this is lame as you'd have to write an explicit instantiation for each possible instantiateion of Array that occurs for your code. So we won't do this.

    The simplest solution is to put the body of operator<< inline in the class definition.

    The other option is to declare that operator<< is a template function.

    Trevor Hickey's code shows one way of doing this, although that has a drawback that Array<A, B>::elements can be accessed by Array<C, D>::operator<<.

    A more secure way is to declare the operator<< before the class:

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

    and then the rest of your code as it was before. Now, the friend declaration in the class will match the pre-existing template, instead of declaring a new non-template.