2

I am trying to write a binary tree. Why does the following code report error C2039, "'<<' : is not a member of 'btree<T>'" even though the << operator has been declared as a friend function in the btree class?

#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;
}
user1232138
  • 5,451
  • 8
  • 37
  • 64
  • 3
    http://sscce.org/ (especially **simple**). – Griwes Mar 26 '12 at 15:47
  • The code appears lengthy but the problem is in class btree and the overloaded << operator function which immediately follows the class. – user1232138 Mar 26 '12 at 15:57
  • @OP, every time when I see so high scroll bar, it indicates wrong testcase. Remove what's necessary and then it will be "Simple, Self-Contained Correct Example". – Griwes Mar 26 '12 at 16:05
  • See [this answer](http://stackoverflow.com/a/6819588/8747). – Robᵩ Mar 26 '12 at 16:06
  • 3
    @Rob: You deleted the things that made this example self-contained. Yes, a lot of that code was unimportant and needed to go, but you cut out too much. – Ben Voigt Mar 27 '12 at 21:16
  • 1
    I disagree, @Ben. Perhaps my edit made everything after your answer's first sentence seem confusing, but that's because everything after the first sentence had nothing to do with the question asked. You're answering the unstated question, "What should I do to fix this specific code?" If that were really the question, I'd have voted to close this as too localized. The question was instead about why we see C2039 despite the friendship, and the specific template instantiation is irrelevant to that. You're of course welcome to edit however you wish, if you think it would improve the question. – Rob Kennedy Mar 27 '12 at 21:57

5 Answers5

7

In

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

you're telling the compiler that there is a non template free function

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

for whatever type you happen to instantiate BTree over. But you never provide such a function. The definition you provide is for a member, but as a member function, your operator<< takes too many parameters.

Given that BTree is a generic type, it shouldn't provide the means of displaying its contained elements; that's up to the contained element type. What would make sense is something like:

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 );
    }
}
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Although I agree with your sentiment, I'm wondering why you're upvoted 3 and me down 2 when I suggested essentially the same thing. :-\ – std''OrgnlDave Mar 29 '12 at 23:48
  • @OrgnlDave: There's more to an answer than being right. Your answer was confusing, and _appeared_ to be wrong (though I now see it was just poor word choice). Sadly, that confusion means it's a poor answer. Fix it up, and maybe they'll get removed. – Mooing Duck Mar 30 '12 at 00:19
  • @MooingDuck Got any suggestions for fixing it up? – std''OrgnlDave Mar 30 '12 at 01:43
3

By declaring the operator friend, you tell the compiler to look for a function

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

where T is the exact same type the btree class template is instantiated with. (e.g. for btree<Node>, the actual signature would be ostream& operator<<(ostream &, Node); -- assuming you hace members i and n of type Node)

This function will have access to private and protected members (variables and functions) of the class btree<T> for all instances of T, but it is not actually a member of the class (as it would be without the friend keyword).

The operator definition you provide is for an operator that is a member of the template class btree, as if you have declared

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

This is due to the btree<T>:: prefix you included (that specifies which class the function/operator belongs to).

Since there is no corresponding operator declaration in the class (see the above description of the friend declaration), the compiler complains.

To fix it, you either

  • keep the friend declaration, remove the btree<T>:: prefix and template<class T> from the operator defintion and change the second parameter type to btree<Type>&, where Type is one of the types you expect the btree template to be instantiated with (e.g. Node) -- then supply similar defintions for other such types as well.
  • or remove the friend keyword from the declaration in the class and remove the T parameter from both the declaration and the definition as now the operator is supposed to work on the whole btree (which is implicitly supplied via *this).
  • Alternatively, you can experiment with declaring the friend operator as a template, but that requires some more modifications: (read more about 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
};

Note that above I assumed that you want to output the whole tree (that is invoke the operator<< on a btree object). It is not clear from the code you have whether this is your intention (class btree does not have members i and n). If not, and the type you want to invoke the << operator on is the actual template parameter of btree, then you don't need to change the second parameter of the templated operator from T, but there is also no need to declare it as friend of class btree as the operator is independent of btree. You do need to declare it as friend of the class whose members i and n you are accessing in the definiton of the operator (e.g Node above), if i and/or n is private in that class. The notion about losing btree<T>:: (or Node::) still applies as the operator does not belong to any class.

Couple more things, assuming you go with the friend declaration:

  • The type of the second parameter to the operator should be btree<T>& (emphasis on &) as it is more efficient to pass a reference to the btree object than to copy the entire btree (or a shallow copy if you use pointers and go with the default copy-contructor)
  • the second parameter should also be marked const, as (presumably) you do not want to change the btree object during output. Be aware that in this case you will need to mark certain non-changing methods in btree<T> as const as well to allow it to compile. (See the FAQ on const correctness)

EDIT'd a few times to make it clear and ensure correctness

Attila
  • 28,265
  • 3
  • 46
  • 55
1

A friend function is granted the same access to members of a class that members get, but it is not a member.

That's the whole point of the friend keyword, to give this access to non-members.

Since your operator<< doesn't use btree<T>, there's no reason to make it a friend of btree<T>.

So I think you meant

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

inside class mydata.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    @user1232138 You're declaring a non-template friend, and you're defining a template member with the wrong number of parameters. – James Kanze Mar 26 '12 at 16:14
  • 1
    @OrgnlDave: He's not trying to write out `btree` objects (that threw me off first too), he's trying to write objects with `i` and `n` members, i.e. `mydata`. – Ben Voigt Mar 27 '12 at 02:33
  • @BenVoigt friend member functions aren't members of the class. You can't provide templated functionality with them unless their function body is given in the header. Period. And what is this mydata class you keep referring to? Is my browser wigging out and not showing me all of the code? – std''OrgnlDave Mar 27 '12 at 20:58
  • @OrgnlDave: You're just repeating what my answer already says. And the `mydata` class was deleted by Rob Kennedy's edit, for no apparent reason. – Ben Voigt Mar 27 '12 at 21:13
  • 1
    @OrgnlDave: Your answer puts member variables in the `btree` class that don't belong there. It's relatively easy to solve any compiler error by replacing the whole program with `int main() { return 0; }`. Getting rid of the error while preserving the intended design is more difficult. – Ben Voigt Mar 28 '12 at 04:20
  • @BenVoigt Why are we arguing over this? Either the editing is correct, and the original question was asking *why he couldn't use a streaming operator with the btree template*, or your interpretation is correct, in that he really wanted to stream mydata. Which would be odd since he put the streaming operator...in btree. It is you, in fact, that redesigned the program by putting the operator<< in a whole different class. You even said "put this in this other class." You didn't answer his question, you told him to do something else. – std''OrgnlDave Mar 28 '12 at 04:27
  • @BenVoigt Furthermore, that 'solution' would only work assuming he ever only wanted to using a class mydata, which defies the point of a template. It could easily be made to use mydata4, through inheritance or not, which has variables of the same name and thus the streaming operator would work fine. I suppose he could change his program, and the intent behind it, and write a friend function for every class he could conceivably want to use that template for...or he could just get his question answered and learn about template friend functions. – std''OrgnlDave Mar 28 '12 at 04:33
  • 1
    @OrgnlDave: `btree` is apparently what was going to call the streaming operator, so it's not entirely unreasonable for something to think it should go there. My answer corrects that mistaken impression. The implementation of the streaming operator makes it perfectly clear that it's supposed to output a `mydata` object, since `mydata` is the only class in the original code (which you can see by checking the question edit history) that has members named `n` and `i`. – Ben Voigt Mar 28 '12 at 14:11
  • 1
    @OrgnlDave: Just because a function will be called from a template class doesn't mean that function should be a template. In this case, it should not. – Ben Voigt Mar 28 '12 at 14:11
  • @BenVoigt Then you go open a question "should I put X in Y or Z?" and be ambiguous as to the purposes of them, and put this answer in, and I'll vote you up. – std''OrgnlDave Mar 28 '12 at 16:42
0

Replace:

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

with:

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

As Ben Voight mentions, the error is telling you that this function is not a member of btree. Also, you seem to be defining that function for mydata exclusively, since it's expecting s and i members.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
0

EDIT

Because it's a friend function, C++ is a bit weird. See, friend functions aren't actually defined in the class, they're defined in a different namespace. You have to provide an output function used by operator<< inside the class definition if you want it to use a templated member function.

The reason I suggest the following method (stream_out) is to demonstrate an easy way to do it in a way that makes it a member and doesn't mislead readers of your code, because it isn't a clever hack.

94% of the time you can use a clever hack such as has been suggested in the comments, but it does not answer the fundamental question: your 'friend' function is not a member of your class unless its body is given in the declaration, period, nothing else to say on the matter.

(What is the other 6% of the time, you ask, that this is not OK? copy constructor nonsense, CLI, and other unspeakable bumps in the night.)

If you absolutely must include it outside, as a member function, you would do something like...

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

EDITED to clear up the summary a bit.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
std''OrgnlDave
  • 3,912
  • 1
  • 25
  • 34
  • is this in the same file? are you sure both are using namespace std? that is *exactly* how I use it and it compiles fine for me. MSVC2k10 – std''OrgnlDave Mar 26 '12 at 22:36
  • @user1232138 Found and (poorly) explained the answer! Friend functions are weird. See answer. – std''OrgnlDave Mar 26 '12 at 23:14
  • 1
    Creating a `stream_out` function is really silly. Just name it `operator<<` like everyone else does and everyone else expects. And why did `i` and `n` move from the `mydata` class to `btree`? – Ben Voigt Mar 27 '12 at 02:33
  • @BenVoigt friend members are not actually members of the class that declares them, they are part of the larger scope. The only way to make templates of them is to provide the functionality inside the class. Making stream_out allows you to put complex logic into your operator<< without having an enormous and ugly class declaration. Also, I don't know what you mean by mydata class? I don't see one in the question... – std''OrgnlDave Mar 27 '12 at 20:56
  • "You have to provide the definition for operator<< inside the class definition if you want it to have a templated version." I'm pretty sure that's wrong. http://ideone.com/n0T8B – Mooing Duck Mar 29 '12 at 17:40
  • @MooingDuck clever (not). A+ for effort, D- for understanding. this is one of many ways to re-write the code so it works, but does not explain the error. you actually ended up saying "here is a function outside the class, give it access." that is great, but doesn't contradict what I said; your function isn't part of the class. Like I said. If you want it to be "part of the class" it has to be in the declaration. I refer you to http://stackoverflow.com/questions/3989678/c-template-friend-operator-overloading – std''OrgnlDave Mar 29 '12 at 23:59
  • @OrgnlDave: My comment was not an answer, nor addressed to the OP. I was merely pointing out that your wording in your answer is misleading and/or wrong. – Mooing Duck Mar 30 '12 at 00:08
  • @MooingDuck You illustrated my point perfectly, though. You forward-declared a function outside the class, a function that is NOT a member, to solve the problem. There's a few ways of doing this. Why is it important that you not do it? In about 94% of the cases it is not. However I'm not one to say hacks are better than understanding. So I'll edit my answer to have slightly different wording, and accept the 2 down votes I received due to your comment. – std''OrgnlDave Mar 30 '12 at 00:14