friend 声明声明了一个非模板函数,未定义的引用

问题描述 投票:0回答:1

我有这样的模板类,其中包含一个friend方法:

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)
{ ... }

该类用于保存图形。 Vertex是一个存储节点的数组,T1是对应的元素类型。 Arcs 是一个 n * n 的二维数组,存储节点之间的路径开销,T2 是对应的元素类型。(注意,n 是模板非类型参数。)

我尝试创建一个

Graph
并调用这样的朋友方法:

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;
}

只得到错误:

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&)'

为什么这里会弹出警告和错误。我该如何解决?

c++11 undefined-reference
1个回答
0
投票

friend
声明为
Graph
的每个实例声明一个名为
operator<<
的常规非模板函数,并将此实例化作为参数。例如,您的程序使用
Graph<int, int, 5>
- 当编译器第一次看到它时,它会生成一个声明

std::ostream & operator<<(std::ostream & os, const Graph<int, int, 5>& g);

请注意,这与您的

operator<<
函数模板是分开且不同的。

然后编译器会看到

std::cout << g
并执行重载决策来为
operator<<
选择最佳重载。由于非模板函数优于函数模板,因此在其他条件相同的情况下,选择由
friend
声明引入的函数。最后,链接器发现这个函数从未被实际实现过;因此出现错误。

最有可能的是,您的意思是与

operator<<
模板参数匹配的
Graph
函数模板的特化应该是该
Graph
特化的友元。你这样表达:

// 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);
};

但是,完全避免

friend
并将
operator<<
委托给成员函数通常更简单:

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.