views:

915

answers:

8

I'm working in Visual Studio 2008 on a C++ programming assignment. We were supplied with files that define the following namespace hierarchy (the names are just for the sake of this post, I know "namespace XYZ-NAMESPACE" is redundant):

(MAIN-NAMESPACE){

      a bunch of functions/classes I need to implement...

      (EXCEPTIONS-NAMESPACE){

            a bunch of exceptions
      }

      (POINTER-COLLECTIONS-NAMESPACE){

            Set and LinkedList classes, plus iterators
      }
}

The MAIN-NAMESPACE contents are split between a bunch of files, and for some reason which I don't understand the operator<< for both Set and LinkedList is entirely outside of the MAIN-NAMESPACE (but within Set and LinkedList's header file). Here's the Set version:

template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set<T>& set)

Now here's the problem: I have the following data structure:

Set A
Set B
Set C
double num

It's defined to be in a class within MAIN-NAMESPACE. When I create an instance of the class, and try to print one of the sets, it tells me that: error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set' (or there is no acceptable conversion)

However, if I just write a main() function, and create Set A, fill it up, and use the operator- it works.

Any idea what is the problem? (note: I tried any combination of using and include I could think of).

+1  A: 

Try calling the function explicitly?

::operator<<( cout, myObj );
SoapBox
A: 

As SoaBox pointed out, try calling it explicitly.

For your information, if you wish to call a global function which has been hidden in the current namespace precede the function with :: to bypass the local function and call the global function.

rboorgapally
A: 

CORRECTION: The text below is based on experience with the g++ family of compilers. After the comment to the answer I have reread the standard (which states that ADL will be used over regular name lookup, and regular name lookup should find the operator<<). I have also tried with comeau compiler (the most standard compliant compiler I know of) and the symbol is found. It seems as a problem with g++ (tried versions 3.3, 4.1, 4.3).

Original answer:

Search for Koening lookup (technically ADL: Argument dependent lookup).

The short answer is that if you have the following class:

namespace test {
    class A {};
}

the stream insertion operator should be defined as:

namespace test {
    std::ostream& operator<<( std::ostream&, A const & );
}

Functions or operators should be defined in the same namespace as one of the arguments that it takes. (*)

When the compiler finds a function call such as:

namespace test2 {
   void g() {
      namespace1::class1 c1;
      namespace2::namespace3::class2 c2;
      f( c1, c2 );
   }
}

it will try to find the f function in the current namespace (at the place of call) or in the enclosing namespaces of c1 and c2 types (namespace1, namespace2::namespace3), but it will not try other namespaces in the search.

(*) In this case, you are pretty much limited to the test namespace, as you are not allowed to add a function to the std namespace (only template specializations).

End of original post.

Even if as commented before this may just be a problem with the compiler, it is common usage and recommended to define all free functions that operate on a user defined type in the same namespace as the type itself.

David Rodríguez - dribeas
Yes, but with the current namespace, it will search its enclosing namespaces too (usually).
jpalecek
+2  A: 

Strange - even though putting free functions associated with a type to a different namespace is a bad practice, the global namespace declarations are always visible.

The only thing I can think of is that declaration with the same name in MAIN-NAMESPACE would shadow the one in the global namespace - isn't there an operator<<, possibly for totally unrelated type, in MAIN-NAMESPACE? If so, you should fix that by using ::operator<< declaration in MAIN-NAMESPACE. Example:

namespace A
{
namespace B
{
  class C{};
}

}

void f(A::B::C*);

namespace A
{
  void f(int*); // try commenting
  using ::f; // these two lines
  void g()
  {
    B::C* c;
    f(c);
  }
}
jpalecek
The operator<< is ONLY in those two files: the one for Set and the one for LinkedList. Both outside of the main namespace.
CS student
A: 

Try calling the function explicitly?

::operator<<( cout, myObj );

Yes, that does work!

it will try to find the f function in the current namespace (at the place of call) or in the enclosing namespaces of c1 and c2 types (namespace1, namespace2::namespace3), but it will not try other namespaces in the search.

So let's see if I got this right: the reason invoking the operator<< from a main() function worked is because I was in the global namespace (as was operator<<). The reason it failed when invoking from the class I implemented is because the class was in a not global namespace and there were no variables in it that pointed the compiler towards the global namespace.

CS student
Not really, the declarations from any namespace are visible in child namespaces, ie. the declarations from the global namespace are visible everywhere (by non-ADL lookup). The reason why it didn't work must be elsewhere.
jpalecek
A: 

OK people asked for a specific examples, so here's the relevant part of the code. //Disclamer: in the slim case someone from my uni sees this, encounters it in the submission file, and decides I copied it or something, my student number is 311670137

This is the header file Set.h:

   namespace MTM {//This is the MAIN-NAMESPACE

    namespace PointerCollections {

        (ITERATORS AND PREDICATE CLASSES)

        template<typename T>
        class Set {
        public:

        /////////////////////////////////
        // Definitions
        /////////////////////////////////

        private:

        /////////////////////////////////
        // Definitions
        /////////////////////////////////

        };


///////////////////////////////////////////////////////////////////////////////
// The implementation part. 
///////////////////////////////////////////////////////////////////////////////
      }
}
// operator<< - the same a Set::print(std::ostream& os,
//                                    const BinaryPredicate<T>& predicate) 
// function called with the 'predicate' parameter omitted
template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MTM::PointerCollections::Set<T>& set){

    set.print(os);
    return os;
}

This is what I defined in a different file:

namespace MTM {
    using std::ostream;

    class Schedule {
    public:
      ///////////////////
      //Definitions, including: 
      ///////////////////
    void registerStation(string stationName);
    void reportRegisteredStations(std::ostream& outputStream) const;

    private:     //My database
               //All the classes Set recieves are defined elsewhere
     Set<RegisteredStation> places;
     Set<BusLine> busses;
     Set<TrainLine> trains;
     double tarifForBuses;
     double tarifForTrains;
    };

}

And here's from the main:

Schedule s();
s.registerStation("1");
s.reportRegisteredStations(cout);//This invokes the error. Definition follows:

reportRegisteredStations is defined as:

void Schedule::reportRegisteredStations(std::ostream& outputStream) const{

     outputStream<<places;
    }
CS student
A: 

This works for me

#include <iostream>
#include <string>
using std::string;

   namespace MTM {//This is the MAIN-NAMESPACE

    namespace PointerCollections {

        template<typename T>
        class Set {
        };


      }
}
template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MTM::PointerCollections::Set<T>& set){

    return os;
}

namespace MTM {
    using std::ostream;
  using PointerCollections::Set;
    class Schedule {
    public:
      ///////////////////
      //Definitions, including: 
      ///////////////////
    void registerStation(string stationName);
    void reportRegisteredStations(std::ostream& outputStream) const;

    private:     //My database
               //All the classes Set recieves are defined elsewhere
        Set<int> places;
        Set<int> busses;
        Set<int> trains;
        double tarifForBuses;
        double tarifForTrains;
    };
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{

        outputStream<<places;
    }

}

int main()
{
  MTM::Schedule s;
  s.reportRegisteredStations(std::cout);
}
jpalecek
Please post compiler and version, as behavior may differ
David Rodríguez - dribeas
+1  A: 

OK I figured this out. jpalecek's intuition about there existing another operator<< in the namespace was correct (apparently I forgot to comment it out).

The lookup rules for namespaces first start the search in the function call's namespace and search up the enclosing namespaces, right up to the global namespace (then it does the Argument dependent lookup if no match is found). However, if along the way it finds some match for operator<<, it stops the search, regardless of the fact that the types used in those functions may be incompatible, as was the case here.

The solution is either to include it into the MAIN-NAMESPACE (which I'm not allowed to), or import it from the global namespace with "using ::operator<<".

CS student