2012-03-26 36 views
2

我想写一个二叉树。为什么下面的代码报告错误C2039,''< <':不是'btree < T>''的成员,尽管< <运算符已被声明为btree类中的友元函数?即使X是Y的朋友,“X不是Y的成员”?

#include<iostream> 
using namespace std; 

template<class T> 
class btree 
{ 
public: 
    friend ostream& operator<<(ostream &,T); 
}; 

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 
+3

http://sscce.org/(尤其是** **简单)。 – Griwes 2012-03-26 15:47:08

+0

该代码看起来很冗长,但问题出现在类btree以及紧随该类之后的重载<<运算符函数中。 – user1232138 2012-03-26 15:57:47

+0

@OP,每当我看到如此高的滚动条时,就表示错误的测试用例。删除必要的内容,然后它将是“简单,自包含的正确示例”。 – Griwes 2012-03-26 16:05:50

回答

3

通过声明操作的朋友,你告诉编译器查找功能

ostream& operator<<(ostream &,T); 

其中T是完全相同的类型B树类模板进行实例化。 (例如:btree<Node>,实际签名会ostream& operator<<(ostream &, Node); - 假设你HACE成员iNoden

此功能将可以访问类btree<T>的private和protected成员(变量和函数)的所有实例T,但它实际上并不是该类的成员(因为它不包含friend关键字)。

操作定义您提供的是一个操作符是模板类B树中的一员,因为如果你有宣布

template<class T> 
class btree 
{ 
public: 
    ostream& operator<<(ostream &,T); 
}; 

这是由于包含的是btree<T>::前缀(即指定功能/操作员属于哪个类)。

由于该类中没有相应的运算符声明(请参阅上述对朋友声明的描述),因此编译器会抱怨。

要解决它,你要么

  • 保持朋友声明,删除操作确定指标btree<T>::前缀和template<class T>并更改第二个参数类型btree<Type>&,其中Type是的一个你期望btree模板被实例化的类型(例如Node) - 然后为其他类型提供类似的定义
  • 或移除声明friend关键字在类和从两个声明定义如现在操作者应该对整个B树工作(可经由隐式地提供除去T参数*this)。
  • 或者,你可以声明朋友经营者作为模板进行实验,但需要更多的修改:(阅读更多关于forward declaration

template<class T> btree; // forward declaration of class btree 

// forward declare operator (or move definition here) 
template<class T> 
ostream& operator<<(ostream &o, btree<T>& s); 

// declare operator as template friend 
template<class T>    
class btree    
{    
public:    
    friend ostream& operator<< <> (ostream &, bree<T>&); 
    // note <> after operator name to denote template with no new template parameters 
}; 

注意,上面我认为你想输出整棵树(即在btree对象上调用operator<<)。从代码中不清楚这是否是你的意图(class btree没有成员in)。 如果不是,并且您想要调用运算符的类型是btree的实际模板参数,则不需要从T更改模板运算符的第二个参数,但是也不需要声明它作为类btree的friend作为运算符独立于btree。如果i和/或n在该类别中是私密的,则您确实需要将其声明为类别的朋友,该类别的成员in您正在访问定义者(例如上面的节点)。关于丢失btree<T>::(或Node::)的概念仍然适用,因为运营商不属于任何类别。

夫妇更多的事情,假设你去与朋友声明:

  • 到操作的类型,第二个参数应该是btree<T>&(强调&),因为它是更有效地传递reference到btree对象比复制整个btree(或使用指针的浅拷贝并使用缺省copy-contructor
  • 第二个参数也应该标记为const,因为(可能)您不希望在更改btree对象期间输出。请注意,在这种情况下,您需要将btree<T>中的某些不变方法标记为const以允许它进行编译。 (参见const correctness的FAQ)

EDIT'd几次说清楚,并确保正确性

1

朋友函数被授予与成员获得的类的成员相同的访问权限,但它不是成员。

这就是关键字friend的全部要点,以便对非成员进行访问。

由于你的operator<<不使用btree<T>,没有理由使它成为btree<T>的朋友。

所以,我认为你的意思

friend ostream& operator<<(ostream &, const mydata&); 

class mydata

+0

但在这种情况下有什么问题? – user1232138 2012-03-26 15:51:03

+1

@ user1232138您正在声明一个非模板的朋友,而您正在使用错误的参数数量定义模板成员。 – 2012-03-26 16:14:34

+0

你几乎达到了标准...看到我的回答 – 2012-03-27 00:05:12

0

替换:

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

有:

ostream& operator<<(ostream &o, const mydata &s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

由于本沃伊特提到,错误是告诉你,这个函数是btree成员。此外,您似乎仅为mydata定义了该功能,因为它期望si成员。

+0

仍然不能正常工作。 – user1232138 2012-03-26 16:03:28

+1

由于该操作员需要访问私人成员,因此它需要成为“类mydata”的“好友”。 – 2012-03-26 16:39:29

7

template <typename T> 
class BTree 
{ 
    // ... 
    friend std::ostream& operator<<(std::ostream&, T); 
    // ... 
}; 

你告诉编译器有一个非模板免费功能

std::ostream& operator<<(std::ostream&, Type) 

你发生任何类型的实例B树了。但是你永远不会提供这样的功能 。您提供的定义适用于成员 ,但作为成员函数,您的operator<<需要太多参数。

鉴于BTree是一个通用类型,它不应该提供 显示其包含的元素的手段;这取决于包含的元素 类型。是什么让感觉是一样的东西:

template <typename T> 
class BTree 
{ 
    struct Node 
    { 
     // ... 
     void display(std::ostream& dest, int indent) const; 
    }; 

    // ... 
    void display(std::ostream& dest) const; 
    friend std::ostream& operator<<(std::ostream& dest, BTree const& tree) 
    { 
     tree.display(dest); 
     return dest; 
    } 
}; 

template <typename T> 
void BTree::display(std::ostream& dest) const 
{ 
    if (myRoot == NULL) { 
     dest << "empty"; 
    } else { 
     myRoot->display(dest, 0); 
    } 
} 

template <typename T> 
void BTree::Node::display(std::ostream& dest, int indent) const 
{ 
    dest << std::string(indent, ' ') << data; 
    if (myLeft != NULL) { 
     myLeft->display(dest, indent + 2); 
    } 
    if (myRight != NULL) { 
     myRight->display(dest, indent + 2); 
    } 
} 
+0

尽管我同意你的观点,但我想知道为什么你会选择3而我选择2,而当我提出基本相同的建议时。 : - \ – 2012-03-29 23:48:23

+0

@OrgnlDave:还有更多的答案,而不是正确的。你的答案令人困惑,并且似乎是错误的(尽管我现在看到它只是很差的词选择)。可悲的是,这种混乱意味着这是一个不好的答案。修复它,也许他们会被删除。 – 2012-03-30 00:19:27

+0

@MooingDuck有任何修复它的建议吗? – 2012-03-30 01:43:26

0

编辑

因为它是一个友元函数,C++是有点怪。请参阅,朋友函数实际上并未在类中定义,它们在不同的名称空间中定义。如果您希望它使用模板化成员函数,则必须在类定义内提供operator<<使用的输出函数。

我推荐以下方法(stream_out)的原因是为了演示一种简单的方法来使它成为一个成员,并且不会误导您的代码的读者,因为它不是一个聪明的黑客。

,你可以使用一个聪明的黑客,如已在意见被提出时

94%,但它并没有回答这个基本的问题:你的“朋友”功能不是你的类的成员,除非它的身体是在声明中给出的,对此没有别的话要说。

(什么是当时的其他占6%,你问,这是不是正常?拷贝构造函数废话,CLI等不可告人的颠簸在夜间。)

如果你绝对必须的外部包括它,作为一个成员函数,你会做类似...

template<class T> 
class btree 
{ 
    private: 
    int i; 
    int n; 
    void stream_out(std::ostream& o); 

    public: 
    friend std::ostream& operator<<(std::ostream& o, btree<T> & me) { 
     me.stream_out(o); 
     return o; 
    } 
}; 

template <class T> 
void btree<T>::stream_out(std::ostream& o) 
{ 
    o << i << '\t' << n; 
} 

编辑清除总结了一下。

+0

仍然是相同的错误... – user1232138 2012-03-26 18:50:29

+0

这是在同一个文件?你确定两者都使用命名空间标准?这就是*完全*我如何使用它,它编译罚款给我。 MSVC2k10 – 2012-03-26 22:36:09

+0

@ user1232138找到并且(很差)解释了答案!朋友功能很奇怪。见答案。 – 2012-03-26 23:14:20

相关问题