“X不是Y的成员”尽管X是Y的朋友?

时间:2012-03-26 15:44:49

标签: c++ templates

我正在尝试编写二叉树。为什么以下代码报告错误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;
}

5 个答案:

答案 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类型的成员nNode

对于btree<T>的所有实例,此函数可以访问类T的私有成员和受保护成员(变量和函数),但它实际上不是该类的成员(因为它会没有friend关键字)。

您提供的运算符定义适用于作为模板类btree成员的运算符,就像您已声明

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

这是由于您包含的btree<T>::前缀(指定了函数/运算符所属的类)。

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

要修复它,你要么

  • 保留好友声明,从运营商 defintion 中删除btree<T>::前缀和template<class T>,并将第二个参数类型更改为{{1其中btree<Type>&是您希望实例化btree模板的类型之一(例如Type) - 然后为其他此类类型提供类似的 defintions
  • 或从类中的声明中删除Node关键字,并从声明中删除friend参数定义现在运算符应该在整个btree上工作(通过T隐式提供)。
  • 或者,您可以尝试将友元运算符声明为模板,但这需要进行一些修改:(了解有关forward declaration的更多信息)

*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。您确实需要将其声明为其成员Tfriend的成员的朋友,您要在运营商的 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定义了该函数,因为它期待si成员。

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

已编辑以清除摘要。