C++-如何为类模板声明函数模板友元



我有一个类模板,它将输出存储在数组中的对象列表。我得到了以下错误,我很困惑错误是在哪里引起的,因为错误在.obj和.exe文件中。

1个未解析的外部文件(proj08.exe第1行)
未解析的外部符号"class std::basic_stream>&__cdecl运算符<<(class std::basic_stream>>,class MyVector)"(?6@YAAAV$basic_ostream@DU$char_traits@D@std@@@std@@AAV01@V$MyVector@N@@@Z) 在函数_main(porj08.obj第1行)中引用

proj08.cpp

#include "stdafx.h"
#include <string>
#include "MyVector.h"
const double FRACTION = 0.5; 
int main()
{
    cout << "nCreating a vector of doubles named Samn";
    MyVector<double> sam;
    cout << "nPush 12 values into the vector.";
    for (int i = 0; i < 12; i++)
        sam.push_back(i + FRACTION);
    cout << "nHere is sam: ";
    cout << sam;
    cout << "n---------------n";
    cout << "nCreating an empty vector named joe";
    MyVector<double> joe;
    // test assignment
    joe = sam;
    cout << "nHere is joe after doing an assignment:n ";
    cout << joe;
    cout << "n---------------n";
    // test the copy constructor
    MyVector<double> bill = sam;
    cout << "nHere is bill after creating it using the copy constructor:n ";
    cout << bill;
    cout << "n---------------n";
    cout << endl;
    system("PAUSE");
    return 0;
}

MyVector.h

#pragma once
#include <iostream>
#include "stdafx.h"
using namespace std;
template <class T>
class MyVector
{
private:
    int vectorSize;
    int vectorCapacity;
    T *vectorArray;
public:
    MyVector() {
        vectorArray = new T[10];
    }
    T size();
    T capacity();
    void clear();
    void push_back(T n);
    T at(int n);
    friend ostream& operator<<(ostream& os, MyVector<T> vt);
    MyVector<T> operator=(MyVector<T>&);
};
/*
 * TEMPLATE FUNCTIONS
 */
//Return array size
template<class T>
T MyVector<T>::size()
{
    return vectorSize;
}
// Return array capacity
template<class T>
T MyVector<T>::capacity()
{
    return vectorCapacity;
}
// clear array values
template<class T>
void MyVector<T>::clear()
{
    for (int i = 0; i < vectorSize; i++)
    {
        vectorArray[i] = '';
    }
    vectorSize = 0;
    vectorCapacity = 2;
}

// Add number to array and double array size if needed
template<class T>
void MyVector<T>::push_back(T n)
{
    int test = 100;
    if (vectorCapacity > vectorSize)
    {
        vectorArray[vectorSize] = n;
        vectorSize++;
    }
    else {
        if (vectorCapacity == 0) {
            vectorArray = new T[4];
            vectorArray[0] = n;
            vectorCapacity = 4;
            vectorSize++;
        }
        else {
            int newCapacity = vectorCapacity * 2;
            // Dynamically allocate a new array of integers what is somewhat larger than the existing array.An algorithm that is often used is to double the size of the array.
            int *tempArray = new int[newCapacity];
            // Change capacity to be the capacity of the new array.
            vectorCapacity = newCapacity;
            // Copy all of the numbers from the first array into the second, in sequence.
            for (int i = 0; i < MyVector::size(); i++)
            {
                tempArray[i] = vectorArray[i];
            }
            delete[] vectorArray;
            vectorArray = new T[newCapacity];
            for (int i = 0; i < MyVector::size(); i++)
            {
                vectorArray[i] = tempArray[i];
            }
            delete[] tempArray;
            // Add the new element at the next open slot in the new array.
            vectorArray[vectorSize] = n;
            // Increment the size;
            vectorSize++;
        }
    }
}
// Return Value and given point in array
template<class T>
T MyVector<T>::at(int n)
{
    return vectorArray[n];
}
// Set one vector to equil another
template<class T>
MyVector<T> MyVector<T>::operator=(MyVector<T>& right) {
    if (vectorCapacity < right.vectorCapacity) {
        if (vectorCapacity != 0)
            delete[] vectorArray;
        vectorArray = new T[right.vectorCapacity];
        vectorCapacity = right.vectorCapacity;
    }
    vectorSize = right.size();
    // Assign values from left to right
    for (int i = 0; i < vectorSize; i++)
    {
        vectorArray[i] = right.at(i);
    }
    return *this;
}
// Cout Vector
template<class T>
ostream& operator << (ostream& os, MyVector<T> vt)
{
    T size = vt.size();
    for (T i = 0; i < size; i++) {
        os << "index " << i << " is " << vt.at(i) << endl;
    }
    return os;
}

如果您想匹配您定义的函数模板,则必须将friend声明为函数模板:

template <typename U> // use U, so it doesn't clash with T
friend ostream& operator<<(ostream& os, MyVector<U> vt);

如果为类模板声明了friend函数,则不会使其成为函数模板

@LogicStuff的答案是完全有效的。我想澄清一下你的代码中到底发生了什么,以及如何使用替代方案来避免它,因为我相信大多数C++程序员至少遇到过一次这个问题。基本上,当模板被实例化时,比如

MyVector<int> something;

然后friend声明自动绑定到模板类型,在本例中为int,因此编译器生成

friend ostream& operator<<(ostream& os, MyVector<int> vt);

然而,这只是一个宣言。您的后一种定义

template<class T>
ostream& operator << (ostream& os, MyVector<T> vt)

int没有任何关系,因为前者更匹配,所以无论何时尝试

cout << something;

编译器尝试调用CCD_ 6版本(其没有定义)。所以你得到了一个链接器错误。

一种广泛使用的替代方法是在类中内联定义操作员,如

friend ostream& operator << (ostream& os, MyVector<T> vt)
{
    T size = vt.size();
    for (T i = 0; i < size; i++) {
        os << "index " << i << " is " << vt.at(i) << endl;
    }
    return os;
}

现在,MyVect的每个实例化都会生成一个绑定到相应模板类型的operator<<的有效定义。请注意,运算符本身不是成员函数,它在全局命名空间中仅通过参数依赖查找(ADL)可见。这个技巧被称为朋友名字注入,在巴顿-纳克曼技巧中广泛使用,你能够成功地使用它,因为在像这样的调用中

cout << something;

呼叫被转换为

operator<< (std::cout, something)

由于something属于MyVector<int>型,因此通过ADL找到了operator<<的定义。如果您的operator<<将例如int作为第二个参数,则无法通过ADL找到它,因为基本类型没有关联的命名空间。

最新更新