1

I am using Microsoft Visual Studio for source code editor, and when I try to compile the below code I got an linker error:

LNK2019 unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class X<int> &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$X@H@@@Z) referenced in function _main Zadatak7.19.04.2021 C:\Users\AT95\Desktop\Zadatak7.19.04.2021\Zadatak7.19.04.2021\main.obj 1

What must I do to make this program work correctly?

I want you to look at the definition of the overloading operator<< because that method is causing errors.

#include <iostream>

template <class T>
class X
{

private:

    int index, capacity;

    T* collection{};
        
public:

    X(const int nmb, const T* array)
    {
        index = 0;

        capacity = nmb;

        collection = new(T[nmb]);

        for (int i = 0; i < nmb; i++)
            collection[index++] = array[i];
    }

    X(const X& other)
    {
        index = other.index;
        capacity = other.capacity;

        collection = new(T[other.capacity]);

        if(other.index < other.capacity)
            for (int i = 0; i < other.index; i++)
                collection[i] = other.collection[i];
    }

    ~X() { delete[] collection; }

    friend std::ostream& operator<<(std::ostream&, X<T>&);

};
        
template <class T>
std::ostream& operator<<(std::ostream& out, X<T>& other)
{
    for (int i = 0; i < other.index; i++)
        out << i + 1 << ". " << other.collection[i];

    return out;
}

int main()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, n = sizeof(array)/sizeof(int);

    X<int> x(n, array), b(x);

    std::cout << x;

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    Have a collection of warnings - live - https://godbolt.org/z/3n841MbMj - working my way through them. – Richard Critten Jul 09 '21 at 16:06
  • Very useful site, I will give my best to check it out. Thanks my friend. I really appreciate your help. – Andrej Trožić Jul 09 '21 at 16:09
  • 2
    @AndrejTrozic regardless of whether that site is used, the hint is to turn on your compiler warnings and read them. They will tell you where your code looks problematic. – Drew Dormann Jul 09 '21 at 16:11
  • The site is also helpful because it is good to see what other compilers think of your code. Different compilers give different diagnostics and one compiler might spot a problem that others don't or explain a problem better. They will generate different output and have different interpretations of [some coding mistakes](https://en.cppreference.com/w/cpp/language/ub) and the discrepancies in their output can help find sneaky bugs. – user4581301 Jul 09 '21 at 16:22
  • [Why do I get linker errors when I use template friends?](https://isocpp.org/wiki/faq/templates#template-friends) – Evg Jul 09 '21 at 16:22

2 Answers2

3

Your friend dclaration is wrong.

    friend std::ostream& operator<<(std::ostream&, X<T>&);

This is a friend declaration of a non templated operator<<.

It should look like this:

    template<typename Z>
    friend std::ostream& operator<<(std::ostream&, X<Z>&);

But there is a better way to declare it. Move the function body into the class then you don't need to declare the template part.

template <class T>
class X
{

    // STUFF AS BEFORE


    // Friend class definition.
    friend std::ostream& operator<<(std::ostream& out, X& other)
    {
        for (int i = 0; i < other.index; i++)
            out << i + 1 << ". " << other.collection[i];

        return out;
    }
}

Secondary note. In output operators the object being output is usually declared const (as outputting it should not modify it).

friend std::ostream& operator<<(std::ostream& out, X const& other)
                                                        ^^^^^
Martin York
  • 257,169
  • 86
  • 333
  • 562
3

You need to write the template keyword at the beginning of the friend function, like this:

#include <iostream>

template <class T>
class X
{

private:

    int index, capacity;

    T* collection{};

public:

    X(const int nmb, const T* array)
    {
        index = 0;

        capacity = nmb;

        collection = new(T[nmb]);

        for (int i = 0; i < nmb; i++)
            collection[index++] = array[i];
    }

    X(const X& other)
    {
        index = other.index;
        capacity = other.capacity;

        collection = new(T[other.capacity]);

        if(other.index < other.capacity)
            for (int i = 0; i < other.index; i++)
                collection[i] = other.collection[i];
    }

    ~X() { delete[] collection; }

    template<class U> friend std::ostream& operator<<(std::ostream&, X<U>&);

};

template <class T>
std::ostream& operator<<(std::ostream& out, X<T>& other)
{
    for (int i = 0; i < other.index; i++)
        out << i + 1 << ". " << other.collection[i];

    return out;
}

int main()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, n = sizeof(array)/sizeof(int);

    X<int> x(n, array), b(x);

    std::cout << x;

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Yeas, that's it. Friend of a class is on his own. Thank you sir ! – Andrej Trožić Jul 09 '21 at 16:27
  • That's all right! – Levon Baghoyan Jul 09 '21 at 16:30
  • When you add `template` to a friend declaration, you make `operator<<(std::ostream&, X&)` a friend for all `U`s. Formally, this approach works, but it is wrong from the architectural point of view - friendship scope should be as narrow as possible. A better solution is to let a compiler know that `operator<<` is a template with a proper forward declaration, as explained [here](https://isocpp.org/wiki/faq/templates#template-friends). – Evg Jul 09 '21 at 16:38