0

I have a following template with inner classes in a map.hpp file:

template<typename Key_T, typename Mapped_T>
class Map {
    // public members of Map ...

    class Iterator {
        //public members of Iterator ...

        friend bool operator!=(const Iterator &i, const Iterator &j) {
            return (i.link != j.link);  
        }

        // private members of iterator ...
        Node * link;
    };
};
#include "map.hxx"   //implementation file for member methods is separate

In the main.cpp I call the following and so far everything works fine:

Map<int, int> x;
// bunch of insertions ...
for (auto it = x.begin; it != x.end(); ++it) {
    // Do something with it ...   
}

However, I want to move the friend function out of the map.hpp file and into the map.hxx file which contains other implementation.

Q: Is it possible to move the free function to a .hxx file and how?

I tired declaring the function as a friend in the Iterator class and did the following in the implementation file:

template<typename Key_T, typename Mapped_T>
bool operator!=(const typename Map<Key_T, Mapped_T>::Iterator & i,
                const typename Map<Key_T, Mapped_T>::Iterator & j) {
    return (i.link != j.link);
}

However it failed with:

$clang++ -std=c++11 -stdlib=libc++ -Wall -Wextra -g main.cpp 

Undefined symbols for architecture x86_64:
  "shiraz::operator!=(shiraz::Map<int, int>::Iterator const&, shiraz::Map<int, int>::Iterator const&)", referenced from:
      _main in main-3oCRAm.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Thanks!

shiraz
  • 1,148
  • 2
  • 11
  • 25

1 Answers1

2

A friend declaration without a corresponding match will inject a new function into the enclosing namespace. This function can only be found via ADL

The easiest way to do what you want is to leave a stub implementation in the iterator class, and have it forward to a "real" function outside. That keeps the nice ADL-only lookup of !=, which solves some thorny overload problems easily. The helper function can simply be a template<class Iterator> and be friended more normally and have a narrow implementation, and not be found via ADL but rather with a fully qualified lookup. Or it could be a static member function of the enclosing map.

template<typename Key_T, typename Mapped_T>
class Map {
  class Iterator;
  static bool it_equal( Iterator const&, Iterator const& );
  class Iterator {
    friend class Map;
    friend bool operator!=(const Iterator &i, const Iterator &j) {
      return !Map::it_equal(i,j);
    }
    friend bool operator==(const Iterator &i, const Iterator &j) {
      return Map::it_equal(i,j);
    }
    Node * link;
  };
};

now your .hxx simply implements bool Map<blah>::it_equal(blah) and you are done.

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • @dyp Mayhap. Btw, while your answer in the other question is good, it doesn't quite apply to operators. Creating a free `template operator!=(T lhs, T rhs)` is highly impolite even if you only specialize it for a few types. And non-deduced context makes it hard for a sub-type to do that trick too, for while the `!=` will be found, it won't be able to deduce the arguments! (I have not tested these beliefs) – Yakk - Adam Nevraumont Mar 18 '15 at 00:27
  • Hm. I intended in my answer to explain the problem and provide *various* solutions. I agree most of them won't be nice for a nested type. Don't quite understand the issue you mentioned wrt subtypes. – dyp Mar 18 '15 at 00:36
  • @dyp by sub-type I mean nested types. A problem may be that `template bool operator!=(Map::iterator, Map::iterator)` cannot be deduced without `blah` being explicit -- will `friend` make it explicit? – Yakk - Adam Nevraumont Mar 18 '15 at 00:49
  • *"without `blah` being explicit"* I'm having a hard time understanding your concern. What do you mean with "explicit"? [Like this?](http://coliru.stacked-crooked.com/a/64d2a67ada0b8238) If so, the friend-declaration probably only makes the name of the primary template visible, not specifically the specialization. I'm not quite sure where the Standard specifies this, maybe in [basic.lookup.argdep]/4.2? – dyp Mar 18 '15 at 22:01
  • @dyp [like this](http://coliru.stacked-crooked.com/a/1dda04f6484c872d) -- probably same situation. Specializations do not impact which function is called, and derived types are not deduced. So you need a `template void foo(T)`, and in an operator that is rude without SFINAE restriction. A big advantage of `friend operator` is eliminating that SFINAE annoyance. – Yakk - Adam Nevraumont Mar 18 '15 at 22:54