0

I have a class CFoo with a private inner class CBar. I want to implement a stream ouput operator for CFoo, which in turn uses a stream output for CBar in it's implementation. I can get this working when CFoo is in the common namespace, but when i place it in a new namespace (namespace foobar), the operator can no longer access the private inner class. I suspect this has something to do with the full signature of the operator, but I can't figure out the correct way to specify the friend declaration and the actual operator declaration so the implementation compiles. Can anyone suggest what I might be missing? Note that it will compile if the stream implementation is done inline in the header, but I hate to expose implementation like this unnecessarily!

in foobar.h (just comment out the usefoobarnamespace to test the non-namespaced version):

#define usefoobarnamespace
#ifdef usefoobarnamespace
namespace foobar
{
#endif // usefoobarnamespace
    class CFoo
    {
    public:
        CFoo() {}
        ~CFoo();
        void AddBar();
    private:
        class CBar
        {
        public:
            CBar() {m_iVal = ++s_iVal;}
            int m_iVal;
            static int s_iVal;
        };

        std::vector<CBar*> m_aBars;

        friend std::ostream& operator<<(std::ostream& rcStream, CFoo& rcFoo);
        friend std::ostream& operator<<(std::ostream& rcStream, CFoo::CBar& rcBar);
    };
    std::ostream& operator<<(std::ostream& rcStream, CFoo& rcFoo);
    std::ostream& operator<<(std::ostream& rcStream, CFoo::CBar& rcBar);
#ifdef usefoobarnamespace
}
#endif // usefoobarnamespace

and in foobar.cpp:

#ifdef usefoobarnamespace
using namespace foobar;
#endif // usefoobarnamespace

int CFoo::CBar::s_iVal = 0;


CFoo::~CFoo()
{
    std::vector<CBar*>::iterator barIter;
    for (barIter = m_aBars.begin(); barIter != m_aBars.end(); ++barIter)
    {
        delete (*barIter);
    }
}

void CFoo::AddBar()
{
    m_aBars.push_back(new CBar());
}


std::ostream& operator<<( std::ostream& rcStream, CFoo& rcFoo )
{
    rcStream<<"CFoo(";
    std::vector<CFoo::CBar*>::iterator barIter;
    for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
    {
        rcStream<<(*barIter);   
    }
    return rcStream<<")";
}

std::ostream& operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
{
    return rcStream<<"CBar("<<rcBar.m_iVal<<")";
}
FlintZA
  • 872
  • 1
  • 11
  • 22

4 Answers4

2

Simply put the code in the .cpp file into the namespace:

namespace foobar {

// your existing code

}
  • Thanks this does work. I prefer to not wrap code in my implementation files in namespaces like this if I can help it, but if I don't find another solution that works I'll do this just for the operators. – FlintZA Aug 21 '09 at 09:54
  • +1 I never understood why people employs the using directive when they define functions/methods that are _inside_ a namespace. – David Rodríguez - dribeas Aug 21 '09 at 09:58
  • @FlintZA Care to share the reason for this rather peculiar preference? It's the simplest, cleanest way of using namespaces. –  Aug 21 '09 at 10:11
  • @Neil we use Whole Tomato's (excellent) Visual Assist. One of it's refactoring tools allows you to automatically create an implementation for a function definition. This automatically generated implementation signature includes the namespace, so using the above approach would require a bit more manual pruning of the code. – FlintZA Aug 21 '09 at 10:30
2

You need to put the operator definitions explicitly in a namespace. (Or fully qualify them with the namespace). The way you are doing it you declare some << operators (that are in namespace foobar), then you define some completely new << operators in the global namespace.

namespace foobar
{
    std::ostream& operator<<( std::ostream& rcStream, CFoo& rcFoo )
    {
        rcStream<<"CFoo(";
        std::vector<CFoo::CBar*>::iterator barIter;
        for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
        {
            rcStream<<(*barIter);   
        }
        return rcStream<<")";
    }

    std::ostream& operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
    {
        return rcStream<<"CBar("<<rcBar.m_iVal<<")";
    }
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
1

Your operator<< functions are now in the foobar namespace, so you should define them as foobar::operator<<.

UncleZeiv
  • 18,272
  • 7
  • 49
  • 77
  • Strangely this works for the CFoo operator, but not for the CFoo::CBar one. std::ostream& foobar::operator<<( std::ostream& rcStream, foobar::CFoo::CBar& rcBar ) { return rcStream<<"CBar("< – FlintZA Aug 21 '09 at 09:50
  • @FlintZA: strange, works for me, I compiled it before answering (g++ 4.3.3). Can you double check? By the way, I found a possible mistake in your code, in (*barIter) you probably want to dereference twice (*(*barIter)) in order to have the CBar overloaded operator called. – UncleZeiv Aug 21 '09 at 11:00
  • @UncleZiev Well cought :) In my actual code case the inner class objects are actually in a map, so I'm dereferencing .second. I missed that in the conversion to a simpler test case. As for this solution not compiling, I'm compiling with Visual Studio 2008, so I suppose it's a quirk of the MS compiler-wouldn't be the first ;) – FlintZA Aug 21 '09 at 12:51
0

The issue can be resolved by specializing the stream operator overloading for the namespace:

std::ostream& foobar::operator<<( std::ostream& rcStream, CFoo& rcFoo )
{
    rcStream<<"CFoo(";
    std::vector<CFoo::CBar*>::iterator barIter;
    for (barIter = rcFoo.m_aBars.begin(); barIter != rcFoo.m_aBars.end(); ++barIter)
    {
        rcStream<<(*barIter);   
    }
    return rcStream<<")";
}

std::ostream& foobar::operator<<( std::ostream& rcStream, CFoo::CBar& rcBar )
{
    return rcStream<<"CBar("<<rcBar.m_iVal<<")";
} 

By default, the global definitions of these functions are getting overloaded. They are not friends of class CFoo and cannot access its private members.

Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93