【问题标题】:C++ - How to declare a function template friend for a class templateC++ - 如何为类模板声明一个函数模板友元
【发布时间】:2016-02-24 23:24:38
【问题描述】:

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

1 未解决的外部(proj08.exe 第 1 行)
未解析的外部符号“class std::basic_ostream > & __cdecl operator &,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 Sam\n";
    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] = '\0';
    }

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

【问题讨论】:

  • 作为旁注,请考虑将MyVector&lt;T&gt; vt 传递给const 参考,friend ostream&amp; operator&lt;&lt;(ostream&amp; os, const MyVector&lt;T&gt;&amp; vt),这样您就不会制作额外的无偿副本。

标签: c++ templates operator-overloading ostream


【解决方案1】:

如果你想匹配你定义的那个,你必须将friend声明为一个函数模板:

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

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

【讨论】:

  • 另一种方法是在类中内联定义它。在这种情况下,函数是通过 ADL 找到的(定义在外部范围内仅通过 ADL 可见)。
  • 另一种选择是根本不使用friend 声明,因为实现只使用公共成员
  • 嗯,就是这样:D
  • 这里唯一的问题是 MyVector 类中的函数也是 MyVector、MyVector 等与模板化类的其他实例的朋友。见web.mst.edu/~nmjxv3/articles/templates.html
【解决方案2】:

@LogicStuff's answer is perfectly valid. 我想澄清一下代码中究竟发生了什么,以及如何使用替代方法来避免它,因为我相信大多数 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;

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

一种广泛使用的替代方法是在类中定义您的运算符内联,例如

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&lt;&lt; 的有效定义。请注意,运算符本身不是成员函数,它只能通过Argument Dependent Lookup (ADL) 在全局命名空间中可见。这个技巧叫做朋友名字注入,在the Barton–Nackman trick中被广泛使用,你可以成功使用它,因为在像这样的调用中

cout << something;

调用被翻译成

operator<< (std::cout, something)

因为something 是MyVector&lt;int&gt; 类型,所以operator&lt;&lt; 的定义是通过ADL 找到的。如果您的 operator&lt;&lt; 采用例如int 作为第二个参数,它不会通过 ADL 找到,因为基本类型没有关联的命名空间。

【讨论】:

  • 所以它与声明不匹配是定义的错误。如果operator&lt;&lt;(朋友)在那个地方直接定义,那么可能是有效的。
猜你喜欢
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多