I have such template class, which includes a friend method:
template<class T1, class T2, int n>
class Graph final
{
private:
std::array<T1, n> vertex;
std::array<std::array<T2, n>, n> arcs;
public:
Graph(const std::array<T1, n> & v, const std::array<std::array<T2, n>, n> & a);
friend std::ostream & operator<<(std::ostream & os, const Graph<T1, T2, n> & g);
};
template<class T1, class T2, int n>
Graph<T1, T2, n>::Graph(const std::array<T1, n> & v, const std::array<std::array<T2, n>, n> & a)
: vertex(v)
{ ... }
template<class T1, class T2, int n>
std::ostream & operator<<(std::ostream & os, const Graph<T1, T2, n> & g)
{ ... }
This class is used to save graphs. Vertex is an array that stores nodes, and T1 is the corresponding element type. Arcs is a two-dimensional array of n * n that stores the path overhead between nodes, and T2 is the corresponding element type.(Note that n is the template non-type parameters.)
And I try to create a Graph
and call friend method like this:
void test() {
using std::array;
array<int, 5> vertex = {100, 200, 300, 400, 500};
array<int, 5> a = {2, 3, 2, 4, 5};
array<array<int, 5>, 5> arcs = {a, a, a, a, a};
Graph<int, int, 5> g(vertex, arcs);
std::cout << g << std::endl;
}
Only get Error:
tree_mst.h:31:87: warning: friend declaration ‘std::ostream& meyok::operator<<(std::ostream&, const Graph<T1, T2, n>&)’ declares a non-template function [-Wnon-template-friend]
friend std::ostream & operator<<(std::ostream & os, const Graph<T1, T2, n> & g);
^
tree_mst.h:31:87: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
../lib/libmtree.so: undefined reference to `operator<<(std::ostream&, Graph<int, int, 5ull> const&)'
../lib/libmtree.so: undefined reference to `Graph<int, int, 5ull>::Graph(std::array<int, 5ul> const&, std::array<std::array<int, 5ul>, 5ul> const&)'
Why are warnings and errors popping up here. How can I solve it?
The friend
declaration declares, for each instantiation of Graph
, a regular non-template function named operator<<
taking this instantiation as a parameter. For example, your program uses Graph<int, int, 5>
- when the compiler first sees that, it produces a declaration
std::ostream & operator<<(std::ostream & os, const Graph<int, int, 5>& g);
Note that this is separate and distinct from your operator<<
function template.
Then the compiler sees std::cout << g
and performs an overload resolution to select the best overload for operator<<
. Since a non-template function is preferred over the function template, other things equal, the function introduced by friend
declaration is selected. Finally, the linker discovers that this function is never actually implemented; hence the error.
Most likely, you meant to say a specialization of operator<<
function template that matches the Graph
template parameters should be the friend of that Graph
specialization. You express it this way:
// Forward declaration
template<class T1, class T2, int n>
class Graph;
// Forward declaration
template<class T1, class T2, int n>
std::ostream & operator<<(std::ostream & os, const Graph<T1, T2, n> & g);
template<class T1, class T2, int n>
class Graph
{
public:
friend std::ostream & operator<< <T1, T2, n>(std::ostream & os, const Graph<T1, T2, n> & g);
};
However, it's usually simpler to avoid friend
altogether, and have operator<<
delegate to a member function:
template<class T1, class T2, int n>
class Graph
{
public:
void print(std::ostream& os) const { /* actual output logic here */);
};
template<class T1, class T2, int n>
std::ostream& operator<<(std::ostream& os, const Graph<T1, T2, n>& g) {
g.print(os);
return os;
}