2011-01-11 152 views
36

我已经阅读了几个有关我的问题在stackoverflow现在,并没有似乎解决我的问题。或者我也许做错了... 超载<<如果我把它变成一个内联函数。但是我怎么让它在我的情况下工作?重载朋友operator <<模板类

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const; 
    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 

template <class classT> 
ostream& operator<<(ostream &os, const D<classT>& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 
+0

有最近关于这可能是有益的一个问题:http://stackoverflow.com/questions/4571611/virtual-operator/ – 2011-01-11 16:52:00

+0

@Daniel - 它不会占用我为模板类重载时遇到的问题 – starcorn 2011-01-11 16:55:12

+5

我认为如果不用给定的答案修改问题会更好。这使得更难确定原来的问题是什么。你可能想在最后加上一个** EDIT **来解决这个问题,但是我发现当问题发生超时并且我不得不拉起历史来查看实际上在第一名。 – 2011-01-11 19:13:12

回答

100

这是那些常见问题有不同的方法是相似的,但不是真的一样。这三种方法在声明你的功能的朋友 - 然后是你如何实现它的方面有所不同。

外向

申报模板作为朋友的所有实例。这就是你已经接受的答案,也是大多数其他答案提出的。在这种方法中,你不必要地通过声明朋友全部operator<<实例来打开你的特定实例D<T>。也就是说,std::ostream& operator<<(std::ostream &, const D<int>&)可以访问D<double>的所有内部。

template <typename T> 
class Test { 
    template <typename U>  // all instantiations of this template are my friends 
    friend std::ostream& operator<<(std::ostream&, const Test<U>&); 
}; 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>&) { 
    // Can access all Test<int>, Test<double>... regardless of what T is 
} 

的内向

只有声明插入运营商的朋友的特定实例。 D<int>在应用于自身时可能会喜欢插入运算符,但它不希望与std::ostream& operator<<(std::ostream&, const D<double>&)有任何关系。

这可以通过两种方式来完成,简单的方法是为@Emery伯杰提出的,这是内联的运营商,可呈现也是其他原因是一个好主意:

template <typename T> 
class Test { 
    friend std::ostream& operator<<(std::ostream& o, const Test& t) { 
     // can access the enclosing Test. If T is int, it cannot access Test<double> 
    } 
}; 

在这第一个版本,您是而不是创建模板operator<<,而是模板的每个实例的非模板函数。再一次,这种差异很微妙,但这基本上等同于手动添加:std::ostream& operator<<(std::ostream&, const Test<int>&)当您实例化Test<int>时,以及另一个类似的过载,当您实例化Testdouble或任何其他类型。

第三个版本比较麻烦。如果没有内联的代码,并通过使用模板,你可以声明模板类的一个朋友的一个实例,而无需打开自己所有其他实例:

// Forward declare both templates: 
template <typename T> class Test; 
template <typename T> std::ostream& operator<<(std::ostream&, const Test<T>&); 

// Declare the actual templates: 
template <typename T> 
class Test { 
    friend std::ostream& operator<< <T>(std::ostream&, const Test<T>&); 
}; 
// Implement the operator 
template <typename T> 
std::ostream& operator<<(std::ostream& o, const Test<T>& t) { 
    // Can only access Test<T> for the same T as is instantiating, that is: 
    // if T is int, this template cannot access Test<double>, Test<char> ... 
} 

以优势外向型

第三个选项和第一个选项之间的细微差别在于你打开其他类的程度。滥用在外向的版本的一个例子是有人希望得到访问到你的内部,并做到这一点:

namespace hacker { 
    struct unique {}; // Create a new unique type to avoid breaking ODR 
    template <> 
    std::ostream& operator<< <unique>(std::ostream&, const Test<unique>&) 
    { 
     // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!! 
     // if Test<T> is an introvert, then I can only mess up with Test<unique> 
     // which is just not so much fun... 
    } 
} 
11

你不能声明这样一个朋友,你需要指定不同的模板类型吧。

template <typename SclassT> 
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs); 

SclassT所以它不会影classT。当定义

template <typename SclassT> 
ostream& operator<< (ostream & os, const D<SclassT>& rhs) 
{ 
    // body.. 
} 
+0

感谢这篇作品使用这段代码编辑了我的问题,我会尽快将这个问题作为答案勾选。 – starcorn 2011-01-11 17:00:25

+4

请注意,这并不是声明`operator <<`作为朋友的特定实例,而是** all **实例,包括模板的任何特殊化。看到答案[这里](http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372) – 2011-01-11 19:11:36

2

这对我没有任何编译器警告的工作。

#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b) 
    return a; 
    else 
    return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
    : d(in) {}; 

    bool operator>(const D& rhs) const { 
    return (d > rhs.d); 
    } 

    classT operator=(const D<classT>& rhs); 

    friend ostream& operator<< (ostream & os, const D& rhs) { 
    os << rhs.d; 
    return os; 
    } 

private: 
    classT d; 
}; 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
0

在这里你去:

#include <cstdlib> 
#include <iostream> 
using namespace std; 

template <class T> 
T my_max(T a, T b) 
{ 
    if(a > b)  
     return a; 
    else 
     return b; 
} 

template <class classT> 
class D 
{ 
public: 
    D(classT in) 
     : d(in) {}; 
    bool operator>(const D& rhs) const { return d > rhs.d;}; 
    classT operator=(const D<classT>& rhs); 

    template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs); 
private: 
    classT d; 
}; 

template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs) 
{ 
    os << rhs.d; 
    return os; 
} 


int main() 
{ 

    int i1 = 1; 
    int i2 = 2; 
    D<int> d1(i1); 
    D<int> d2(i2); 

    cout << my_max(d1,d2) << endl; 
    return 0; 
} 
0

我觉得你不应该让朋友摆在首位。

您可以创建一个公共方法调用打印,这样的事情(对非模板类):

std::ostream& MyClass::print(std::ostream& os) const 
{ 
    os << "Private One" << privateOne_ << endl; 
    os << "Private Two" << privateTwo_ << endl; 
    os.flush(); 
    return os; 
} 

,然后在类外(但在同一个命名空间)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

我认为它也适用于模板类,但我还没有测试过。