C++中的朋友和模板

Posted

技术标签:

【中文标题】C++中的朋友和模板【英文标题】:friend and template in C++ 【发布时间】:2018-07-15 12:53:57 【问题描述】:

我的 C++ 代码示例中有一个大问题。 “朋友”和“模板”有问题。

错误消息:Matrix.h:26:79:警告:

朋友声明'std::ostream& matrixClass::operator

Matrix.h:26:79: 注意:

  (if this is not what you intended, make sure the function template

已经声明过,这里在函数名后面加上)

Matrix.h:28:77:警告:

  friend declaration 'matrixClass::Matrix<T>*

matrixClass::operator*(const matrixClass::Matrix&, const matrixClass::Matrix&)' 声明一个非模板函数 [-Wnon-template-friend] 朋友矩阵*运算符*(const Matrix &m1, const Matrix &m2);

Matrix.cpp:1:0:

C:\Users\Peter\CLionProjects\PK\untitled76\Matrix.h:26:79:警告: 朋友声明 'std::ostream& matrixClass::operator

Matrix.h:26:79: 注意:

  (if this is not what you intended, make sure the function template

已经声明过,这里在函数名后面加上)

Matrix.h:28:77:警告:

  friend declaration 'matrixClass::Matrix<T>*

matrixClass::operator*(const matrixClass::Matrix&, const matrixClass::Matrix&)' 声明一个非模板函数 [-Wnon-template-friend] 朋友矩阵*运算符*(const Matrix &m1, const Matrix &m2);

CMakeFiles\untitled76.dir/objects.a(main.cpp.obj):在函数“main”中:

main.cpp:8: 未定义的引用 main.cpp:8: 未定义引用 matrixClass::Matrix<int>::Matrix(int)'<br> main.cpp:10: undefined reference tomatrixClass::Matrix::set(int, int, int)' main.cpp:11: 未定义引用 matrixClass::Matrix<int>::set(int, int, int)'<br> main.cpp:12: undefined reference tomatrixClass::Matrix::set(int, int, int)' main.cpp:13: 未定义引用matrixClass::Matrix<int>::set(int, int, int)'<br> main.cpp:15: undefined reference tomatrixClass::operator main.cpp:15: 未定义引用 matrixClass::operator<<(std::ostream&, matrixClass::Matrix<int> const&)'<br> main.cpp:8: undefined reference tomatrixClass::Matrix::~Matrix()' main.cpp:8:未定义对 `matrixClass::Matrix::~Matrix()'的引用

代码: Matrix.h

#ifndef MATRIX_H_
#define MATRIX_H_

#include <iostream>

namespace matrixClass 

    template<class T>
    class Matrix 
    private:
        int dimension;
        T **m;
    public:
        Matrix(int d);

        Matrix(const Matrix &original);

        ~Matrix();

        void set(int x, int y, T value);

        T get(int x, int y) const;

        int getDimension() const;

        friend std::ostream &operator<<(std::ostream&, const Matrix<T> &matrix);

        friend Matrix<T>* operator*(const Matrix<T> &m1, const Matrix<T> &m2);
    ;


#endif

Matrix.cpp

#include "Matrix.h"

using namespace matrixClass;

template<class T>
Matrix<T>::Matrix(int d)
        : dimensiond, mnew T *[d] 
    //m = new T*[d];

    for (int i = 0; i < d; i++) 
        m[i] = new T[d];
    


// COPY-CONSTRUCTOR
template<class T>
Matrix<T>::Matrix(const Matrix &original)
        : dimensionoriginal.dimension,
          mnew T *[original.dimension] 
    for (int i = 0; i < dimension; i++) 
        *(m + i) = *(original.m + i);
    


// DESTRUCTOR
template<class T>
Matrix<T>::~Matrix() 
    for (int i = 0; i < dimension; i++) 
        delete[] m[i];
    
    delete[] m;


template<class T>
void Matrix<T>::set(int x, int y, T value) 
    m[x][y] = value;


template<class T>
T Matrix<T>::get(int x, int y) const 
    return m[x][y];


template<class T>
int Matrix<T>::getDimension() const 
    return dimension;


template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) 
    int dimension = matrix.getDimension();

    for(int x = 0; x < dimension; x++) 
        for(int y = 0; y < dimension; y++) 
            output << matrix.get(x, y) << " ";
        
        return output;
    


template<class T>
Matrix<T>* operator*(const Matrix<T>& m1, const Matrix<T>& m2) 
    int dimension = m1.getDimension();
    Matrix<T>* m = new Matrix<T>(dimension);

    for(int x = 0; x < dimension; x++) 
        for(int y = 0; y < dimension; y++) 
            T value = 0;
            for(int i = 0; i < dimension; i++) 
                value += m1.get(x, i) * m2.get(i, y);
            
            m->set(x, y, value);
        
    
    return m;

main.cpp

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

using namespace matrixClass;
using namespace std;

int main() 
    Matrix<int> m(2);

    m.set(0, 0, 1);
    m.set(0, 1, 2);
    m.set(1, 0, 3);
    m.set(1, 1, 4);

    cout << m << "*" << endl << m << "=" << endl;

    return 0;

【问题讨论】:

模板(在您的情况下为矩阵)应在头文件中包含所有声明和定义。先试试吧。 完整错误输出;这不是最小的;但要说的不仅仅是“它不起作用”,请点赞 让你的朋友函数也模板化,并更好地在标题中移动模板化的成员函数 *(m + i) = *(original.m + i) 将在原始和副本之间共享内存。它们的破坏将删除这些内存区域的两倍。 @O'Neil 你是什么意思?你觉得有错吗? 【参考方案1】:

这个答案解决了您的非会员operator&lt;&lt;()operator*() 使用朋友模板的问题,这与将函数模板的实例设为朋友略有不同(@songyuanyao 提出的第二种解决方案)。不同之处在于,对于朋友模板,模板函数的所有实例都是类Matrix&lt;&gt; 的朋友。在这种情况下,我认为没有任何实际区别,但在其他情况下,可能有。因此,我想我还是会展示它。

我是这样想的。这两个运算符都是非成员函数,这意味着它们独立于 Matrix&lt;&gt; 类,因此请独立考虑它们的原型:

template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix);

template<class T>
Matrix<T>* operator*(const Matrix<T>& m1, const Matrix<T>& m2);

现在,将T 替换为U,因为要使它们的所有实例成为Matrix&lt;&gt; 的朋友,它们的原型需要包含在Matrix&lt;&gt; 的类定义中,这也是一个模板,而且它已经是使用T 作为其模板参数。

template<class U>
std::ostream& operator<<(std::ostream& output, const Matrix<U>& matrix);

template<class U>
Matrix<U>* operator*(const Matrix<U>& m1, const Matrix<U>& m2);

现在,您可以让他们成为Matrix&lt;&gt; 的朋友。您所需要的只是适当的语法:

template<class T>
class Matrix

    ...
    template<class U>
    friend std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

    template<class U>
    friend Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);
;    

最后,它们都需要在matrixClassnamespace 内,并且它们的声明和定义需要以正确的顺序排列,以便每个人都知道其他的存在:

namespace matrixClass 

// BEGIN Forward declarations

template<class T>
class Matrix;

template<class U>
std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

template<class U>
Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);

// END Forward declarations

template<class T>
class Matrix

    ...
    template<class U>
    friend std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

    template<class U>
    friend Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);
;

...

template<class U>
std::ostream& operator<<(std::ostream& output, const Matrix<U>& matrix)

    ...   // your implementation goes here


template<class U>
Matrix<U>* operator*(const Matrix<U>& m1, const Matrix<U>& m2)

    ...   // your implementation goes here


   // end of namespace matrixClass

所有这些模板代码都应该在头文件 Matrix.h 中,正如已经提到的那样。

另外,我认为您应该将operator*() 的原型更改为返回Matrix&lt;U&gt; 而不是Matrix&lt;U&gt;*。它将表现得更像普通的operator*(),它可以让您执行以下操作:Matrix&lt;int&gt; m = m1*m2;m = m1*m2*m3;。此外,您班级的用户不必担心删除由operator*() 分配的内存或动态分配的性能成本。只需在operator*() 中创建一个本地的Matrix&lt;U&gt; 变量并按值返回即可;让返回值优化 (RVO) 处理剩下的事情。

【讨论】:

【参考方案2】:

friend 声明中operator&lt;&lt; 指的是一个非模板函数,而它的定义说它是一个模板函数;他们不匹配。

您可以使用friend 声明内联定义它(作为非模板函数):

template<class T>
class Matrix 
    ... ...
    friend std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) 
        int dimension = matrix.getDimension();

        for(int x = 0; x < dimension; x++) 
            for(int y = 0; y < dimension; y++) 
                output << matrix.get(x, y) << " ";
            
            return output;
        
    
    ... ...
;

或者引用函数模板来声明friend

// class declaration
template<class T>
class Matrix;

// function declaration
template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix);

// class definition
template<class T>
class Matrix 
    ... ...
    friend std::ostream& operator<< <T>(std::ostream& output, const Matrix<T>& matrix);
    ... ...
;

// function definition
template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) 
    int dimension = matrix.getDimension();

    for(int x = 0; x < dimension; x++) 
        for(int y = 0; y < dimension; y++) 
            output << matrix.get(x, y) << " ";
        
        return output;
    

关于未定义的引用错误,见Why can templates only be implemented in the header file?

【讨论】:

尝试作为内联,但仍有错误未定义对 `matrixClass::Matrix::Matrix(int) 的引用 @PeterHerrlich 你看到我链接的帖子了吗? Why can templates only be implemented in the header file? en.cppreference.com/w/cpp/language/friend 有类似的例子。有趣的是,这不仅可以通过 friend 模板 来解决,还可以通过 templatefriend 来解决。 @A.A 注意有区别;模板友元声明使类的函数模板友元的所有实例化。好吧,如果这也符合 OP 的意图,那就没问题了。

以上是关于C++中的朋友和模板的主要内容,如果未能解决你的问题,请参考以下文章

聊聊 C# 和 C++ 中的 泛型模板 底层玩法

Sun Studio C++ 中模板类的模板友元失败

C++ 派生模板类:访问实例的受保护成员

c++ 模板

多个目标文件中的 C++ 模板和编译

小白学习C++ 教程十五C++ 中的template模板和泛型