两个模板类的非模板友元函数未定义引用错误

3

问题陈述:

使用2个不同的模板类1个矩阵类和另一个向量实现类,并使用1个友元函数,即multiply函数。

错误位置

 multiply(matA,p);
in main.cpp 

问题: 由于我正在使用非模板函数作为带有类参数的模板化类,因此我遇到了错误。
undefined reference to `multiply(matrix<int>, vectorimp<int>)'
collect2.exe: error: ld returned 1 exit status

错误:

**** Build of configuration Debug for project Matrix_Vector_Multiplication ****

**** Internal Builder is used for build               ****
g++ -oMatrix_Vector_Multiplication.exe Vector.o Matrix_Vector_Multiplication_main.o Matrix_Vector_Multiplication.o
Matrix_Vector_Multiplication_main.o: In function `main':
D:\C++ Eclipse projects\Matrix_Vector_Multiplication\Debug/../Matrix_Vector_Multiplication_main.cpp:25: undefined reference to `multiply(matrix<int>, vectorimp<int>)'
collect2.exe: error: ld returned 1 exit status
Build error occurred, build is stopped
Time consumed: 1741  ms.  

带有.h和.cpp文件的矩阵类:

matrix.h

#pragma once
#include <iostream>
#include<vector>
#include <time.h>
#include <ostream>
#include "Vector.h"
#define LENGTH 3
#define WIDTH  3

//using namespace std;
template <typename T>
class vectorimp;
template <typename T>
class matrix
  {
private:
    T rows ;
    T cols ;
    T g[LENGTH];
    T **mat;

public:
    //Default constructor
    matrix(T rows , T cols);
    ~matrix();
    T **generatematrix(int rows, int cols);
    void populatematrix(T *src, T size);
    void print();
    template<class T>
    friend void multiply(matrix<T> p, vectorimp<T> v);
  };

matrix.cpp

#include "Matrix_Vector_Multiplication.h"
#include <omp.h>
#include  <stdio.h>
#include <iostream>


using namespace std;
template <class T>
matrix<T>::matrix (T rows , T cols) : rows(rows),cols(cols) {
    this ->mat = generatematrix(this ->rows ,this ->cols );
}
template <class T>
matrix<T>::~matrix()
{
    for(int i=0; i< this->rows; i++)
    {
        delete[] this ->mat[i];
    }
}
template <class T>
T **matrix<T> ::generatematrix (int rows, int cols){
    T **temp = new int*[rows];

    for(int i =0; i< rows; i++)
    {
        temp[i] = new int[cols];
    }
    return temp;
}
template <class T>
void matrix<T> ::print()
{
    for(int i=0;i<rows;i++)
    {
        for(int j =0; j<cols; j++)
        {
            std::cout<<mat[i][j]<<" ";
        }
        cout<<endl;
    }
}
template <class T>
void matrix<T>::populatematrix(T *src, T size)
{
    if (rows * cols !=size){
        cout<<"size of matrix is not equal to size of array"<< endl;
        exit(-1);
    }
    int pos =0;
    for(int i=0;i<rows; i++){
        for(int j=0;j<cols; j++){
            this->mat[i][j]=src[pos++];
        }
    }
}
template <class T>
void multiply (matrix<T> p, vectorimp<T> v)
{
  #pragma omp parallel
   int g[3];
   for (int i=0;i<3;i++){
        g[i]=0;
   }
   //multiplication.
 //  clock_t start = clock();
      #pragma omp for
   for(int i=0;i<3;i++)
   {
       for(int j=0;j<3;j++)
       {
          // std::cout << "I am here "<< (v.vec[i][j])<<std::endl;
            g[i] = g[i]+( p.mat[i][j] * v.vec[j]);

       }
        std::cout << "I am here "<< g[i]<<std::endl;
  /*  clock_t stop = clock();
        printf("Computing time = %0.9fus\n",
               double(stop - start)/CLOCKS_PER_SEC);*/
  }
}

template class matrix<int>;

vector.h

#pragma once
#include <iostream>
#include<vector>
#include <time.h>
#include <ostream>
#include "Matrix_Vector_Multiplication.h"

template <typename T>
class matrix;
template <typename T>
class vectorimp
  {
private:
    int vec[3];
    T vec3D[3][3];
    T size;
    T recent;

public:
    //Default constructor
    vectorimp();
    // Destructor
    ~vectorimp();
    // function to get assign desired values to the vector
    void populate_vector1D(std::vector <std::vector<T> > &data);
    template<class T>
    friend void multiply(matrix<T> p, vectorimp<T> v);
  };

vector.cpp

#include "Vector.h"
#include <iostream>

using namespace std;

template <class T>
vectorimp<T>::vectorimp(){
    //vec = vec[4][4];
     size = 1;
     recent =0;
}
template <class T>
vectorimp<T>::~vectorimp(){}

template <class T>
void vectorimp<T>::populate_vector1D(std::vector <std::vector<T> > &data)
{
   for (unsigned int i = 0; i < data.size(); i++)
   { // printing the 2D vector.
      for (unsigned int j = 0; j < data[i].size(); j++)
      {
        vec[i]   = data[i][j];
      }
   }
}
template class vectorimp <int>;

main.cpp文件

#include "Matrix_Vector_Multiplication.h"
#include <iostream>
#include "Vector.h"

using namespace std;

int main()
{

      int srcA[]= {2,4,3,1,5,7,0,2,3};
      matrix<int> matA(3,3);
      matA.populatematrix (srcA,9);
      std::vector<std::vector<int> > v{ { 2,4,3 },
        { 5,1,6 },
        { 6,3,2 } };
        vectorimp<int> p;
        p.populate_vector1D(v);
        multiply(matA,p);
        return 0;
}

说实话,我不是编码方面的专家,所以在使用模板时会感到困惑。非常感谢提前的帮助。

免责声明:

Checked out the avaiable resources  like 
https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl
https://dev59.com/g3M_5IYBdhLWcg3wfTS7
https://dev59.com/O3RB5IYBdhLWcg3w1Kr0
and few more 

3
您忘记了显式特化“multiply”。 - Daniel
2
template <class T> void multiply (matrix<T> p, vectorimp<T> v) != void multiply(matrix<T> p, vectorimp<T> v) - NathanOliver
@NathanOliver 非常感谢,但是如果我从 template <class T> void multiply (matrix<T> p, vectorimp<T> v) 中删除 template <class T>,它将无法工作,因为这些参数都是模板。 - john
@NathanOliver,您能否提供一些解决方案或方法来解决这个问题?我一直无法找到它的解决办法。 - john
2
看看Dani的评论。这就是为什么你应该把所有的模板代码放在头文件中的原因。 - NathanOliver
显示剩余8条评论
2个回答

0

我有两个建议想提出来,首先,由于你正在使用模板,请将头文件和cpp文件放在一起。其次,请看下面的代码行:

vectorimp<T>::~vectorimp(){}

析构函数中你什么也没做,这很危险,请尝试添加一些类似于 delete[] vec 的内容。同时请注意上一行代码:

vec = vec[4][4];

看起来你正在尝试初始化一个二维向量,但在你的声明中:

int vec[3]

危险……

T vec3D[3][3];

你声明了这个变量,但是你没有用构造函数初始化它。 然后你调用了这个变量:

vectorimp<int> p;

哪个将您的vec3D初始化为nullptr。 最后,您决定将此nullptr对象传递给populate1D方法: 并进行以下调用:

vec[i] = data[i][j]

因为vec为空指针,程序崩溃了。


这里没有nullptr问题。如果data元素比vec中保留的空间更多,vec[i] = data[i][j]会导致问题,但是populate1D验证了data的大小(data.size()data[i].size()),因此在这种情况下不会出现nullptr问题。然而,由于这是一个非常幼稚的设计,还有许多其他问题。 - Manuel
但它被声明为int vec[3],这不是一个nullptr,只是一个由3个int组成的数组。 - Manuel
哎呀,我没看到,虽然这种解决问题的方式对我来说很新,但我从来没有见过有人用向量初始化静态数组的元素??? - Yunfei Chen

0
你正在一个 cpp 文件中定义 multiply(matrix<int>, vectorimp<int>),这是一个独立的编译单元,因此在该文件之外不可见。模板在使用/需要时被实例化,但如果它在另一个编译单元中使用,则编译器没有函数体,因此它是未定义的。 你必须将函数体放在头文件中,以便所有需要它的编译单元(cpp 文件)都可以使用函数体。
template <class T>
void multiply (matrix<T> p, vectorimp<T> v);

template <typename T>
class matrix
  {
public:
    friend void multiply<>(matrix<T> p, vectorimp<T> v);
  };

template <class T>
void multiply (matrix<T> p, vectorimp<T> v)
{
    // whatever
}

class matrix和/或class vectorimp之前声明friend函数,然后告诉编译器它是一个模板函数。你不能把template<class T>放在class内的友元声明中,因为它会遮蔽模板参数,所以只需使用multiply<>(带角括号)。

你还有其他错误,比如没有创建复制构造函数。当multiply函数接收到matrixvectorimp参数的副本时,当函数返回时,这些副本将被delete,当程序结束时,你将有一个双重删除。

如果通过引用传递参数,则不会出现双重删除。

matrix::~matrix中,你必须删除mat以避免内存泄漏。

    for (int i=0; i < this->cols; i++)
    {
        delete [] this->mat[i];
    }
    delete [] this->mat;

你正在为一个未知的类型 T 分配 int
T **temp = new int*[rows];

这应该是:

T **temp = new T*[rows];

你正在将索引与模板类型混合使用:

T rows ;
T cols ;

它们与参数类型无关。

如果您进行这些更改,它将在没有内存泄漏的情况下运行:


manuel@desktop:~/projects$ g++ -Wall main.cc -o main -std=c++17 && valgrind --leak-check=full ./main
==16701== Memcheck, a memory error detector
==16701== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16701== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==16701== Command: ./main
==16701== 
I am here 36
I am here 47
I am here 18
==16701== 
==16701== HEAP SUMMARY:
==16701==     in use at exit: 0 bytes in 0 blocks
==16701==   total heap usage: 13 allocs, 13 frees, 73,932 bytes allocated
==16701== 
==16701== All heap blocks were freed -- no leaks are possible
==16701== 
==16701== For counts of detected and suppressed errors, rerun with: -v
==16701== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

在声明和循环中,还有许多硬编码的数组索引(值为3),如果main中的matrix和/或vector大小发生变化,这些索引将会出错。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接