其他分享
首页 > 其他分享> > c-模板运算符的奇怪行为<<

c-模板运算符的奇怪行为<<

作者:互联网

我无法理解运算符的行为.在我的课上:

标题:

#ifndef VECTOR_H_
#define VECTOR_H_

#include <string>
#include <iostream>

template<class T>
class Vector {
        static const int EXPANDER = 10;
        T* array;
        int next;
        int length;
        void expand();
        void contract();
    public:
        Vector();
        Vector(const Vector& v);
        void add(const T e);
        T get(int index) const;
        bool removeByIndex(int index);
        bool remove(T e);
        int size() const;

        T operator[](int i) const;
        T& operator+=(const T& t);
        T operator+(const T& s);

        friend std::ostream& operator<< (std::ostream& os, const Vector<T>& obj);
        friend std::istream& operator>> (std::istream& is, Vector<T>& obj);

        std::string toString();
        ~Vector();
};

#endif /* VECTOR_H_ */

vector.cpp

#include "Vector.h"
#include <string>
#include <sstream>

template<class T>
Vector<T>::Vector() {
    length = EXPANDER;
    next = 0;
    array = new T[EXPANDER];
}

template<class T>
Vector<T>::Vector(const Vector& v) {
    length = v.next + 1 + EXPANDER;
    next = v.next;
    array = new T[length];
    for (int i = 0; i <= v.next; i++) {
        array[i] = v.array[i];
    }
}

template<class T>
void Vector<T>::add(const T e) {
    if (next >= length - 1)
        expand();
    array[next++] = e;
}

template<class T>
T Vector<T>::get(int index) const {
    if (index > next)
        return -1;
    return array[index - 1];
}

template<class T>
bool Vector<T>::removeByIndex(int index) {
    if (index > next)
        return false;
    for (int i = index; i < length; i++) {
        array[i] = array[i + 1];
    }
    next--;
    contract();
    return true;
}

template<class T>
bool Vector<T>::remove(T e) {
    int index = -1;
    for (int i = 0; i < next; i++) {
        if (array[i] == e) {
            index = i;
            break;
        }
    }
    if (index == -1)
        return false;
    return removeByIndex(index);
}

template<class T>
int Vector<T>::size() const {
    return next;
}

template<class T>
void Vector<T>::expand() {
    length += EXPANDER;
    T* temp = new T[length];
    for (int i = 0; i < next; i++) {
        temp[i] = array[i];
    }
    delete[] array;
    array = temp;
}

template<class T>
void Vector<T>::contract() {
    if (next + EXPANDER >= length)
        return; // NO need to contract

    length = next + EXPANDER + 1;
    T* temp = new T[length];
    for (int i = 0; i < next; i++) {
        temp[i] = array[i];
    }
    delete[] array;
    array = temp;
}

template<class T>
T Vector<T>::operator[](int i) const {
    return get(i);
}

template<class T>
T& Vector<T>::operator+=(const T& t) {
    for (int i = 0; i < t.size(); i++) {
        add(t.get(i));
    }
    return *this;
}

template<class T>
T Vector<T>::operator+(const T& s) {
    this += s;
    return this;
}

template<class T>
std::ostream& operator<< (std::ostream& os, Vector<T>& obj) {
    os << obj.toString();
    return os;
}

template<class T>
std::istream& operator>> (std::istream& is, Vector<T>& obj) {
    int size;
    T temp;
    is >> size;
    for (int i = 0; i < size; i++) {
        is >> temp;
        add(temp);
    }
    return is;
}

template<class T>
std::string Vector<T>::toString() {
    using namespace std;
    ostringstream sb;
    sb << "Elements(" << size() << "): [";
    for (int i = 0; i < next; i++) {
        sb << array[i] << ", ";
    }
    string r;
    r = sb.str();
    r = r.substr(0, r.size() - 2) + string("]");
    return r;
}

template<class T>
Vector<T>::~Vector() {}

我用main.cpp运行此代码

#include "Vector.h"
#include "Vector.cpp"
#include <string>
#include <iostream>
using namespace std;
int main() {
    Vector<int> v;
    v.add(1);
    v.add(2);
    cout << v << endl;
}

魔力在于操作符<<标头中的声明.如果我删除CONST修饰符,则编译器会说:未定义对operator<<的引用,但使用const即可.有趣的是,在我的实现中,在cpp中,我没有CONST. 顺便说一句,如何解决带有警告的警告:朋友声明为运算符声明了非模板函数?

解决方法:

您应该学习如何将其简化为Short, Self-Contained, Compilable Example aka最小工作示例.

这是一个说明问题的SSCCE:

#include <iostream>

template<class T>
class Vector
{
private:
    T m;

public:
    Vector(T p) : m(p) {}

    friend std::ostream& operator<<(std::ostream& o, Vector<T> const& v);

    T get() const { return m; }
};

template<class T>
std::ostream& operator<<(std::ostream& o, Vector<T>& v)
{
    // accessing a private member leads to a compiler error here:
    return o << "[function template]" << /*v.m*/ v.get();
}

// remove this function to get the same behaviour as in the OP
std::ostream& operator<<(std::ostream& o, Vector<int> const& v)
{
    return o << "function" << v.m;
}

int main()
{
    Vector<int> v(42);
    std::cout << v;
}

请注意,它只有大约30行长,可以放在一个没有滚动条的屏幕上.

现在,问题基于您的类模板中的friend声明:

friend std::ostream& operator<<(std::ostream& o, Vector<T> const& v);

查找名为operator<<的函数.在周围的范围内,与这个已经存在的功能成为朋友.但是找不到与那些参数类型匹配的任何参数.因此,它在周围的(=全局)命名空间中声明了一个新函数.该函数如下所示:

std::ostream& operator<<(std::ostream& o, Vector<T> const& v);

(在全局名称空间中)
注意:如果仅通过好友声明进行声明,则只能通过依赖于参数的查找来找到它.

现在,您稍后再声明一个具有相同名称的功能模板.但是当您之前在类模板中编写friend声明时,编译器无法知道您打算与该函数模板成为朋友.因此,朋友功能和功能模板这两个是不相关的.

现在发生的是通常的重载解决方案.如果不添加const,则首选函数模板,因为使用非const参数调用它:

Vector<int> v;
v.add(1);
v.add(2);
cout << v << endl; // v is not const

对于类型为Vector< int>的自变量,与Vector< int>&功能模板(专业化)的“优先”性优于与Vector的结合.常量和放大器;朋友功能.因此,选择功能模板,该模板具有定义(功能主体),并且所有内容均可编译,链接和工作.请注意,该函数模板不为好友,但这不会引发错误,因为您不使用任何私有成员.

将const添加到函数模板后,函数模板不再是参数的更好匹配.因为我们有一个函数和一个带有相同重载“ rank”的函数模板,所以首选非模板.因此,调用了朋友函数,该函数没有定义=>发生链接器错误.

最简单的解决方案是在类定义内定义friend函数:

template<class T>
class Vector
{
private:
    T m;

public:
    Vector(T p) : m(p) {}

    friend std::ostream& operator<<(std::ostream& o, Vector<T> const& v)
    {
        return o << v.m;
    }
};

使用前向声明的解决方案:

template<class T>
class Vector;

template<class T>
std::ostream& operator<<(std::ostream& o, Vector<T> const& v);

template<class T>
class Vector
{
private:
    T m;

public:
    Vector(T p) : m(p) {}

    friend std::ostream& operator<< <T>(std::ostream& o, Vector<T> const& v);
};

template<class T>
std::ostream& operator<<(std::ostream& o, Vector<T> const& v)
{
    return o << v.m;
}

现在,编译器可以找到前向声明的函数模板,并与该现有函数(函数模板的专业化)成为朋友,而不用声明新函数.

一个与整个功能模板友好的解决方案:

template<class T>
class Vector
{
private:
    T m;

public:
    Vector(T p) : m(p) {}

    template<class U>
    friend std::ostream& operator<<(std::ostream& o, Vector<U> const& v);
};

template<class T>
std::ostream& operator<<(std::ostream& o, Vector<T> const& v)
{
    return o << v.m;
}

在此解决方案中,friend声明声明了一个函数模板,并且在类定义之后的命名空间范围内的后续声明重新声明了该函数模板.

标签:c,templates,operator-overloading,friend
来源: https://codeday.me/bug/20191010/1883362.html