Friend template overloaded operator <<: unre

2019-07-08 03:42发布

问题:

I'm having issues with the error

Error LNK2019 unresolved external symbol "class std::basic_ostream > & __cdecl cop4530::operator<<(class std::basic_ostream > &,class rob::Stack const &)" (??6rob@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV12@ABV?$Stack@H@0@@Z) referenced in function _main Project7 c:\Users\Robrik\documents\visual studio 2015\Projects\Project7\Project7\post.obj 1

Right now, all that post is doing is calling the operator<<

The declaration

namespace rob {     

template < typename T> class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
    void print(std::ostream& os, char ofc = ' ') const;
private:
    std::vector<T> arr;
};

The definition

template < typename T>
inline std::ostream & rob::operator<<(std::ostream & os, const Stack<T>& a)                {     
    return a.print(os, ' ');
}
template<typename T>
inline void rob::Stack<T>::print(std::ostream & os, char c) const
{
    for (int i = 0; i != arr.size(); i++)
    {
        os << c << arr[i];
    }
    os << '\n';
}

They are located in a .h file and a .hpp respectively, I require that the operator is not a member function (for assignment).

回答1:

You should as well declare function signature inside rob namespace which it actually belongs:

namespace rob {
template <typename T>
class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
};

template < typename T>
std::ostream& operator<< (std::ostream& os, const Stack<T>& a){
    ...
}


回答2:

The issue with the code sample;

template <typename T>
class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
    void print(std::ostream& os, char ofc = ' ') const;
    // ...
};

Is that the operator<< is being declared as a non-template function. For every type T used with Stack, there needs to be a non-template operator<<. For example, if there is a type Stack<int> declared, then there must be an operator implementation as follows;

std::ostream& operator<< (std::ostream& os, const Stack<int>& a) {/*...*/}

Since it is not implemented, the linker fails to find it and results in the error you get.

As a side note; gcc warns about this as follows

warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]

note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)

This is probably not what is intended, that every instants atom has its own implementation.

To correct this, you can declare a template operator before the Stack type and then declare as a friend, an instantiation. The syntax looks a little awkward, but is looks as follows;

// forward declare the Stack
template <typename>
class Stack;

// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Stack<T>&);

template <typename T>
class Stack {
    friend std::ostream& operator<< <>(std::ostream& os, const Stack<T>& a);
    // note the required <>        ^^^^
    void print(std::ostream& os, char ofc = ' ') const;
    // ...
};

template <typename T>
std::ostream& operator<<(std::ostream&, const Stack<T>&)
{
  // ... implement the operator
}

The above code limits the friendship of the operator to the corresponding instantiation of Stack, i.e. the operator<< <int> instantiation is limited to access the private members of the instantiation of Stack<int>.

Alternatives include allowing the friendship to extend to all instantiations of the templates;

template <typename T>
class Stack {
    template <typename T1>
    friend std::ostream& operator<<(std::ostream& os, const Stack<T1>& a);
    // ...
};

The implementation for the operator<< could then be done inline inside the class definition, or outside.



回答3:

In addition to @LibertyPaul's answer you need to add a template<T> to friend std::os... line, in order to work:

namespace rob {

   template <typename T> class Stack {
        friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a) {
              return a.arr.print(os, ' ');
        }
   };

}