6

I've some problem with get me weired. I've typedef'ed a std::vector which contains some own class:

typedef std::vector<data::WayPoint> TWayPointList; 

This is a nested type inside the structure DataHandler which resists in some namespace data.

So, now I want to print out the single contents of the vector. For this, my idea was to overload the << operator and loop through the single elements of typedef'ed vector. So I declared the following output operator inside the structure DataHandler:

namespace data { structure DataHandler { // ... some code typedef std::vector<data::WayPoint> TWayPointList; // ... some more code /** * @brief Globally overloaded output operator * * @param[in] arOutputStream Reference to output stream. * @param[in] arWayPointList WayPoint which should be printed to output stream. */ LIB_EXPORTS friend std::ostream& operator<<(std::ostream& arOutputStream, const data::DataHandler::TWayPointList& arWayPointList); } // structure DataHandler } // namespace data 

and defined it in the respective source file:

namespace data { std::ostream& operator<<(std::ostream& arOutputStream, const DataHandler::TWayPointList& arWayPointList) { for(DataHandler::TWayPointList::const_iterator lIterator = arWayPointList.begin(); lIterator < arWayPointList.end(); ++lIterator) { arOutputStream << *lIterator << std::endl; } return arOutputStream; } } // namespace data 

This compiles fine. But if I add something like this

int main(int argc, char *argv[]) { // create Waypoint data::WayPoint lWayPoint; // create waypoint list data::DataHandler::TWayPointList lWayPointList; // append two elements lWayPointList.push_back(lWayPoint); lWayPointList.push_back(lWayPoint); std::cout << lWayPointList << std::endl; return 0; } 

in my testmain.cpp, the compiler mentions, that it couldn't find the correct operator<< (and make a lot of assumptions, which one it has found...including some of my own defined in other classes). Some error like this

src/main.cpp:107: error: no match for 'operator<<' in 'std::cout << lWayPointList' src/main.cpp:107:18: note: candidates are: ... a long list of canditates... 

I think it has something to do with ADL, but I didn't get the point.

So, any ideas and adivce to get the code work?

[edit] I've added a few files to the source code and the error output for clarifying.

6
  • 3
    Please copy/paste the actual compiler error/warning you're talking about. Commented Oct 9, 2012 at 20:20
  • 1
    Are you deliberately confusing data and mkilib? Commented Oct 9, 2012 at 20:20
  • Do you have the operator <<(ostream&, const WayPoint&) defined? Commented Oct 9, 2012 at 20:21
  • If data is a namespace then your operator<< definition must go inside of said namespace. Commented Oct 9, 2012 at 20:22
  • @KerrekSB Uhm, yes - that has happend at copying code to stackoverflow - sorry. I've corrected this. Commented Oct 9, 2012 at 20:39

2 Answers 2

6

The friend declaration declares a function at namespace level in the namespace of the class that has such friend declaration. From the definition of the operator it seems that you are defining it in the global namespace (incidentally what your comment in the friend declaration says, too bad compilers don't read comments). You need to define the operator<< in the correct namespace:

std::ostream& mkilib::operator<<(std::ostream& arOutputStream, /*^^^^^^^^*/ const mkilib::DataHandler::TWayPointList& arWayPointList) 

or alternatively:

namespace mkilib { std::ostream& operator<<(std::ostream& arOutputStream, const DataHandler::TWayPointList& arWayPointList) {...} } 

In your program there were two operator<< declared that took the TWayPointList object, one in the global namespace (the definition is a self declaration) and the one in the ::mkilib namespace (from the friend declaration). Argument dependent lookup was finding the one in ::mkilib, but that was never defined in code.


After the update it seems that this is not really the issue, as the compiler is not able to find the overload (the answer above was about code that compiled but not linked). There is something that you have changed from your code to what you ask regarding the namespaces. If Waypoint and the operator<< that takes the std::vector<Waypoint> are defined in the same namespace, then ADL will find the correct overload. Note that the namespace where DataHandler is defined does not have any effect.


Actually now that I think about it, the original answer does apply. The friend declaration does not have any effect on lookup as ADL won't look inside DataHandler searching for that operator, so the only declaration of operator<< is the self-declaration in the definition.

Note that a friend declaration declares an entity at namespace level, but the declaration is only visible inside the class that has the friend declaration.

Piece of advice: Avoid using directives, they only bring confusion and pain. If needed, reopen the namespace or qualify identifiers... using directives make reasoning about lookup much more complex.

Sign up to request clarification or add additional context in comments.

5 Comments

Mhm, sorry, I didn't get it. Could you explain it a bit furhter? Oh, and I've added a few lines to my code example above...
@nightsparc: It seems that this is not really your issue, as the error message you added says that the compiler is not finding the overload, rather than the linker not finding the definition. There is something that changes from your code to what you asked or you are missing an #include or something alike, as ADL should be able to find the overload if it is defined in the same namespace that the type stored in the container. I have updated the answer.
@nightsparc: Updated yet again, the original answer applies albeit for a different reason. The declaration inside DataHandler will not be seen by ADL as DataHandler is not one of the arguments to operator<< in the call, so the only declaration of operator<< is in the global namespace.
Rodriguez: Thanks for your very detailled answer. I got the point :-). Problem was, that my WayPoint class and the typedef'ed vector hadn't resist in the same namespace (the class resists in some child namespace of data). For this, ADL weren't able to find the correct overload. I've changed this. Now the typedef is part of the same namespace as the WayPoint class definition - and the correct overload is found. Now that I'm thinking about, the coupling of the class and the vector-typedef inside the same namespace seem more natural as the splitting I've done before...
@nightsparc: Note that the typedef is not important at all. The compiler will resolve the typedef to the real type std::vector<ns::Waypoint> and the namespaces of the template and the template arguments (i.e. ::std and ::ns in this case) will be checked during ADL. The typedef can reside in any namespace, but only those of the real type will be searched.
0

As I see from your code snippets that you are not using namespace data but your operator is defined in data namespace. You could do use data namespace but I am sure you have your own reasons.

Instead of declaring an operator specific to your data type, you can define a generic operator outside of your namespace like this:

template<typename T> std::ostream& operator<<(std::ostream& out, const std::vector<T>& list) { for(std::vector<T>::const_iterator iter = list.begin(); iter != list.end(); ++iter) { out << *iter; } } 

1 Comment

A library that overloads operator<< for all vector types is certainly a bad one.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.