我正在尝试编写二叉树。为什么以下代码报告错误C2039,“'<<' :不是'btree< T>'“的成员,即使<<<运算符已在btree类中声明为友元函数?
#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;
}
答案 0 :(得分:7)
在
template <typename T>
class BTree
{
// ...
friend std::ostream& operator<<( std::ostream&, T );
// ...
};
你告诉编译器有一个非模板自由函数
std::ostream& operator<<( std::ostream&, Type )
无论您遇到什么类型实例化BTree。但你永远不会
提供这样的功能。您提供的定义是针对会员的,
但作为会员功能,您的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 );
}
}
答案 1 :(得分:3)
通过声明操作员朋友,告诉编译器查找函数
ostream& operator<<(ostream &,T);
其中T
与btree类模板实例化的类型完全相同。 (例如,对于btree<Node>
,实际签名为ostream& operator<<(ostream &, Node);
- 假设您拥有i
类型的成员n
和Node
对于btree<T>
的所有实例,此函数可以访问类T
的私有成员和受保护成员(变量和函数),但它实际上不是该类的成员(因为它会没有friend
关键字)。
您提供的运算符定义适用于作为模板类btree成员的运算符,就像您已声明
一样template<class T>
class btree
{
public:
ostream& operator<<(ostream &,T);
};
这是由于您包含的btree<T>::
前缀(指定了函数/运算符所属的类)。
由于类中没有相应的运算符声明(请参阅上面的友元声明描述),编译器会抱怨。
要修复它,你要么
btree<T>::
前缀和template<class T>
,并将第二个参数类型更改为{{1其中btree<Type>&
是您希望实例化btree模板的类型之一(例如Type
) - 然后为其他此类类型提供类似的 defintions Node
关键字,并从声明和中删除friend
参数定义现在运算符应该在整个btree上工作(通过T
隐式提供)。
*this
注意上面的我假设您要输出整个树(即在btree对象上调用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<<
和i
)。 如果不是,并且要调用n
运算符的类型是btree的实际模板参数,则不需要更改模板化运算符的第二个参数{ {1}},但也没有必要将其声明为类btree的<<
,因为运算符独立于btree。您确实需要将其声明为其成员T
和friend
的成员的朋友,您要在运营商的 definiton 中访问(例如上面的节点),如果{{1 }}和/或i
在该类中是私有的。关于丢失n
(或i
)的概念仍然适用,因为运算符不属于任何类。
假设你和朋友声明一起去做更多的事情:
n
(强调btree<T>::
),因为将reference传递给btree对象比复制整个对象更有效btree(如果使用指针,则使用浅层副本,并使用默认的copy-contructor)Node::
,因为(推测)您不希望在输出期间更改btree对象。请注意,在这种情况下,您需要将btree<T>&
中的某些不变方法标记为&
以允许它进行编译。 (请参阅const correctness上的常见问题解答)编辑了几次以明确并确保正确
答案 2 :(得分:1)
友元函数被授予与成员获得的类成员相同的访问权限,但它不是成员。
这是friend
关键字的全部要点,以便为非成员提供此访问权限。
由于operator<<
未使用btree<T>
,因此没有理由将其设为btree<T>
的朋友。
所以我认为你的意思是
friend ostream& operator<<(ostream &, const mydata&);
在class mydata
内。
答案 3 :(得分: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;
}
正如Ben Voight所提到的,错误告诉您此功能不是btree
的成员。此外,您似乎只为mydata
定义了该函数,因为它期待s
和i
成员。
答案 4 :(得分:0)
修改
因为它是朋友的功能,所以C ++有点奇怪。看,友元函数实际上没有在类中定义,它们是在不同的命名空间中定义的。如果希望它使用模板化成员函数,则必须在类定义中提供operator<<
使用的输出函数。
我建议使用以下方法(stream_out)的原因是为了演示一种简单的方法,使其成为一个成员并且不会误导代码的读者,因为它不是一个聪明的黑客。 / p>
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;
}
已编辑以清除摘要。