Friend function inside a class template

93 views Asked by At
#include <iostream>

template <typename type, int size>
class testClass
{
    type num[size];
public:
    testClass(std::initializer_list<type> list)
    {
        int i = 0;
        for (auto elem : list) {
            num[i] = elem;
            i++;
        }
    }

    friend std::ostream& operator<< (std::ostream &stream, const testClass<type, size>& test);
};

template<typename type, int size>
std::ostream& operator<< (std::ostream &stream, const testClass<type, size>& test)
{
    for (int i = 0; i < size; i++) {
        stream << test.num[i];
    }
}

int main()
{
    testClass<int, 3> test{ 1, 2, 3 };

    std::cout << test;
}

My code results in a linking error even though I seem to have correctly declared a friend function inside a templated class. This is a simplified program to demonstrate the problem.

I know there are multiple solutions to this and one of them is by writing template <typename type, int size> before the friend declaration. My question is, why does this work?

I am new to template programming and so it's just tough to grasp why this works and why my original code does not work.

2

There are 2 answers

0
Vlad from Moscow On

This declaration

friend std::ostream& operator<< (std::ostream &stream, const testClass<type, size>& test);

declares a non-template friend function. It is the class itself that is a template but not the friend function.

You need either to define it within the definition of the class test as for example (such a friend function defined in a template class is called templated entity):

template <typename type, int size>
class testClass
{
    type num[size];
public:
    testClass(std::initializer_list<type> list)
    {
        int i = 0;
        for (auto elem : list) {
            num[i] = elem;
            i++;
        }
    }

    friend std::ostream& operator<< (std::ostream &stream, const testClass<type, size>& test)
    {
        for (int i = 0; i < size; i++) {
            stream << test.num[i];
        }

        return stream;
    }
};

or for each used specialization of the class test for which the friend function is called to define the non-template friend function outside the class as for example

std::ostream& operator<< (std::ostream &stream, const testClass<int, 3>& test)
{
    for (int i = 0; i < 3; i++) {
        stream << test.num[i];
    }

    return stream;
}

Pay attention to that your friend function shall return a reference to the stream.

Here are demonstration programs.

#include <iostream>
#include <initializer_list>

template <typename type, int size>
class testClass
{
    type num[size];
public:
    testClass( std::initializer_list<type> list )
    {
        int i = 0;
        for (auto elem : list)
        {
            num[i] = elem;
            i++;
        }
    }

    friend std::ostream &operator<< ( std::ostream &stream, const testClass<type, size> &test )
    {
        for (int i = 0; i < size; i++)
        {
            stream << test.num[i];
        }

        return stream;
    }
};

int main()
{
    testClass<int, 3> test1{ 1, 2, 3 };
    std::cout << test1 << '\n';

    testClass<int, 5> test2{ 1, 2, 3, 4, 5 };
    std::cout << test2 << '\n';
}

and

#include <iostream>
#include <initializer_list>

template <typename type, int size>
class testClass
{
    type num[size];
public:
    testClass( std::initializer_list<type> list )
    {
        int i = 0;
        for (auto elem : list)
        {
            num[i] = elem;
            i++;
        }
    }

    friend std::ostream &operator<< ( std::ostream &stream, const testClass<type, size> &test );
};

std::ostream &operator<< ( std::ostream &stream, const testClass<int, 3> &test )
{
    for (int i = 0; i < 3; i++)
    {
        stream << test.num[i];
    }

    return stream;
}

std::ostream &operator<< ( std::ostream &stream, const testClass<int, 5> &test )
{
    for (int i = 0; i < 5; i++)
    {
        stream << test.num[i];
    }

    return stream;
}

int main()
{
    testClass<int, 3> test1{ 1, 2, 3 };
    std::cout << test1 << '\n';

    testClass<int, 5> test2{ 1, 2, 3, 4, 5 };
    std::cout << test2 << '\n';
}

The output of the both programs is

123
12345
0
Ted Lyngmo On

Your friend function is not a function template so the definition outside the class is the definition for a separate, non-friend function template. The function you declared a friend will fail linking because no definition for it can be found.

You could fix it by making the friend declaration into a function template declaration:

template <class T, int S>
friend std::ostream& operator<<(std::ostream& stream,
                                const testClass<T, S>& test);

It's however usually easier to just define the friend function in the class definition:

friend std::ostream& operator<<(std::ostream& stream,
                                const testClass<type, size>& test) {
    for (int i = 0; i < size; i++) {
        stream << test.num[i];
    }
    return stream; // don't forget to return the stream
}