简体   繁体   English

模板运算符的奇怪行为

[英]Strange behavior of templated operator<<

I cant understand a behavior of operator<< in my class: 我在课堂上无法理解operator <<的行为:

header: 标头:

#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() {}

and i run this code with main.cpp 我用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;
}

the magic is in operator<< declaration in header. 魔术在标题中的operator<<声明中。 if i delete CONST modifier, compiler says: Undefined reference to operator<< , but with const it works. 如果我删除CONST修饰符,则编译器会说: Undefined reference to operator<< ,但使用const可以正常工作。 It is interesting that in my implementation, in cpp, I doesn't have CONST. 有趣的是,在我的实现中,在cpp中,我没有CONST。

btw, how to solve warnings with warning: friend declaration declares a non-template function for operators? 顺便说一句,如何解决带有warning: friend declaration declares a non-template function为操作员warning: friend declaration declares a non-template function

You should learn how to boil this down to a Short, Self-Contained, Compilable Example aka Minimal Working Example. 您应该学习如何将其简化为一个简短的,自包含的,可编译的示例,也称为“最小工作示例”。

Here's an SSCCE that demonstrates the problem: 这是一个说明问题的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;
}

Note that it's only about 30 lines long and fits onto one screen w/o scroll bars. 请注意,它只有大约30行长,可以放在一个没有滚动条的屏幕上。


Now, the problem is based upon the friend declaration inside your class template: 现在,问题基于您的类模板中的friend声明:

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

This looks up a function named operator<< in the surrounding scopes, to befriend this already existing function. 这会在周围的范围内查找一个名为operator<<的函数,以与该已存在的函数成为朋友。 But it doesn't find any that matches those parameter type. 但是找不到与那些参数类型匹配的任何参数。 Therefore, it declares a new function in the surrounding (= global) namespace. 因此,它在周围的(=全局)命名空间中声明了一个新函数。 This function looks like this: 该函数如下所示:

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

(in the global namespace) Note: it can only be found via Argument-Dependent Lookup if it's only declared via a friend-declaration. (在全局名称空间中)注意:如果仅通过好友声明进行声明,则只能通过依赖于参数的查找来找到它。

Now, you later declare a function template of the same name. 现在,您稍后再声明一个具有相同名称的功能模板 But the compiler cannot know that you meant to befriend this function template when you wrote the friend declaration inside you class template before . 但是,当您之前在类模板中编写friend声明时,编译器无法知道您打算与该函数模板成为朋友。 So those two, the friend function and the function template, are unrelated . 因此,朋友功能和功能模板这两个是不相关的

What happens now is the usual overload resolution. 现在发生的是通常的重载解决方案。 The function template is preferred if you don't add a const, since you call it with a non-const argument: 如果不添加const,则首选函数模板,因为使用非const参数调用它:

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

for this argument of type Vector<int> , the binding to Vector<int>& of the function template (specialization) is preferred over the binding to the Vector<int> const& of the friend function. 该参数类型的Vector<int> ,结合到Vector<int>&函数模板(专门化),优选在结合于的Vector<int> const&的朋友的功能。 Hence, the function template is chosen, which has a definition (function body) and everything compiles, links and works. 因此,选择功能模板,该模板具有定义(功能主体),并且所有内容均可编译,链接和工作。 Note that the function template is not befriended, but this doesn't raise an error since you don't use any private members. 请注意,该函数模板不为好友,但这不会引发错误,因为您不使用任何私有成员。

Once you add the const to the function template, the function template is no longer a better match for the argument. const添加到函数模板后,函数模板不再是参数的更好匹配。 As we have a function and a function template with the same overload "rank", the non-template is preferred. 因为我们有一个函数和一个带有相同重载“ rank”的函数模板,所以首选非模板。 Ergo, the friend function is called, which has no definition => a linker error occurs. ergo,该朋友函数被调用,它没有定义=>发生链接器错误。


The simplest solution is to define the friend function inside the class definition: 最简单的解决方案是在类定义内定义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;
    }
};

A solution using forward-declarations: 使用前向声明的解决方案:

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

Now, the compiler can find the forward-declared function template and befriend this existing function (the specialization of the function template) instead of declaring a new function. 现在,编译器可以找到前向声明的函数模板,并与该现有函数(函数模板的专业化)成为朋友,而不用声明新函数。


A solution befriending the whole function template: 一个与整个功能模板友好的解决方案:

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

In this solution, the friend-declaration declares a function template, and the later declaration at namespace scope following the class' definition redeclares this function template. 在此解决方案中,friend声明声明了一个函数模板,并且在类定义之后的命名空间范围内的后续声明重新声明了该函数模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM