模板化操作符的奇怪行为<< [英] Strange behavior of templated operator<<

查看:163
本文介绍了模板化操作符的奇怪行为<<的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不能理解操作符的行为<<在我的课程:



标题:

  #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操作符[](int i)const;
operator + =(const T& t);
T operator +(const T& s);

friend std :: ostream&运算符<< (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

  #includeVector.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 [下一个++] = 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 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 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 temp [i] = array [i];
}
delete [] array;
array = temp;
}

template< class T>
void Vector< T> :: contract(){
if(next + EXPANDER> = length)
return; //无需合同

length = next + EXPANDER + 1;
T * temp = new T [length];
for(int i = 0; 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&向量< T> :: operator + =(const T& t){
for(int i = 0; i< t.size(); i ++){
add(t.get );
}
return * this;
}

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

template< class T>
std :: ostream&运算符<< (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;
是>>尺寸;
for(int i = 0; i 是>>温度
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 <下一个; i ++){
sb < array [i]<< ,;
}
string r;
r = sb.str();
r = r.substr(0,r.size() - 2)+字符串(]);
return r;
}

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

with main.cpp

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

神奇的是运算符< code>标题中的声明。如果我删除CONST修饰符,编译器说:未定义的引用操作符< ,但使用const它工作。有趣的是,在我的实现中,在cpp中,我没有CONST。



btw,如何使用为操作符声明一个非模板函数

解决方案

你应该学习如何将a 短,自包含,可编译的示例 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&运算符<<<<<<<(std :: ostream& o,Vector< T>& v)
{
//访问私有成员会导致编译器错误:
return o< < [功能模板]< /*v.m*/ v.get();
}

//删除此函数以获得与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行长,适合一个屏幕w / o






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

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

这会查找名为的运算符<< 在周围的范围内,来支持这个已经存在的函数。但它没有找到任何匹配那些参数类型。因此,它在周围(=全局)命名空间中声明一个新函数。此函数如下所示:

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

(在全局命名空间中)
注意:它只能通过参数依赖



现在,您稍后会声明一个名称相同的函数模板。但是,当你在类模板之前之前编写好友声明时,编译器不能知道你是否是这个函数模板的朋友。所以那两个,朋友函数和函数模板,是不相关的

现在发生的是通常的重载分辨率。如果你不添加一个const,那么函数模板是首选的,因为你使用一个非const参数来调用它:

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

Vector< int> ,绑定到函数模板(specialization)的 Vector< int>& 比绑定到向量< int> const& 。因此,选择函数模板,它有一个定义(函数体),并编译,链接和工作。请注意,函数模板不是友好的,但这不会引发错误,因为你不使用任何私有成员。



一旦你添加 const 到函数模板,函数模板不再是参数的更好匹配。因为我们有一个具有相同重载rank的函数和函数模板,所以优选非模板。 Ergo,朋友函数被调用,它没有定义=>发生链接器错误。






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

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

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

friend std :: ostream&运算符<<<(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&运算符<< < T>(std :: ostream& o,Vector< T> const& v);
};

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



现在,编译器可以找到向前声明的函数模板,函数(函数模板的特殊化)而不是声明一个新函数。






 模板< class T> 
class Vector
{
private:
T m;

public:
向量(T p):m(p){}

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

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



在此解决方案中,friend声明声明一个函数模板,在命名空间范围的声明,遵循类定义重命名此函数模板。


I cant understand a behavior of operator<< in my class:

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

#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. if i delete CONST modifier, compiler says: Undefined reference to operator<<, but with const it works. It is interesting that in my implementation, in cpp, I doesn't have CONST.

btw, how to solve warnings with warning: friend declaration declares a non-template function for operators?

解决方案

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:

#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.


Now, the problem is based upon the friend declaration inside your class template:

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. 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. 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:

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. 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. As we have a function and a function template with the same overload "rank", the non-template is preferred. Ergo, the friend function is called, which has no definition => a linker error occurs.


The simplest solution is to define the friend function inside the class definition:

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.

这篇关于模板化操作符的奇怪行为&lt;&lt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆