Search code examples
c++c++11templatesc++17friend

Can't figure out linker error in VS2019 for friend in templated class


Please note - StackOverflow found a dozen similar examples to this one. I went through every one and still can't get my code to compile. This may be similar to another question, but if you say it is - please make sure that the techniques shown in the example allow getting this code to compile. Thanks in advance.

I'm playing around with some basic code trying to get better as a student of C++. I'm sure this is a simple error/mistake, but I can't figure it out:

#include <iostream>
#include <string>

// General templated class
template <typename T>
class AddElements {
private:
    T lhs;
    T rhs;
public:
    AddElements(T lhs_init=T(), T rhs_init=T()): lhs{lhs_init}, rhs{rhs_init} { }
    T add() { return lhs + rhs; }

    friend std::istream& operator>>(std::istream&, const AddElements&);
};

template <typename T>
std::istream& operator>>(std::istream& istr, const AddElements<T>& ae) {
    istr >> ae.lhs;
    istr >> ae.rhs;
    return istr;
}

int main() {
    std::size_t lines;
    std::string line_types;
    AddElements<int> mycol_ints;

    std::cin >> lines;
    std::cin >> mycol_ints;

    return 0;
}

I'm using Visual Studio 2019's C++ compiler:

$env:cl = "/EHsc /Wall /external:anglebrackets /external:W3 /external:templates- /experimental:external /RTCsu /sdl /analyze /std:c++17"

cl classtemplates.cpp
(...)
/out:classtemplates.exe
classtemplates.obj
classtemplates.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class AddElements<int> const &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$AddElements@H@@@Z) referenced in function _main
classtemplates.exe : fatal error LNK1120: 1 unresolved externals

Pointers to the fine manual welcome. I've spent a while Googling around and looking at examples on cppreference.com but for now I'm stumped.


Solution

  • The friend declaration refers to a non-template operator, while the definition is a template operator, they don't match.

    You might want

    template <typename T>
    class AddElements;
    template <typename T>
    std::istream& operator>>(std::istream& istr, AddElements<T>& ae);
    
    template <typename T>
    class AddElements {
    private:
        T lhs;
        T rhs;
    public:
        AddElements(T lhs_init=T(), T rhs_init=T()): lhs{lhs_init}, rhs{rhs_init} { }
        T add() { return lhs + rhs; }
    
        friend std::istream& operator>> <>(std::istream&, AddElements&);
        //                              ^^
        // refer to the instantiation of the template operaotr>>
    };
    
    template <typename T>
    std::istream& operator>>(std::istream& istr, AddElements<T>& ae) {
        istr >> ae.lhs;
        istr >> ae.rhs;
        return istr;
    }
    

    Or you can make it non-template and define it inline in the class

    template <typename T>
    class AddElements {
    private:
        T lhs;
        T rhs;
    public:
        AddElements(T lhs_init=T(), T rhs_init=T()): lhs{lhs_init}, rhs{rhs_init} { }
        T add() { return lhs + rhs; }
    
        friend std::istream& operator>> (std::istream& istr, AddElements& ae) {
            istr >> ae.lhs;
            istr >> ae.rhs;
            return istr;
        }
    };
    

    BTW: The parameter ae should be declared as non-const; it's supposed to be modified in operator>>.