5

I have a templated class, Iterable; for which I want to overload the begin() and end() free functions. It stores data as a vector of unique_ptr, but the interface uses boost::indirect_iterator for convenience.

My code builds and runs under CLang-3.5, but I tried on g++-4.9 and it did not. But I do not know why ? (And which compiler has the right behaviour).

template<typename T>
using SimpleVec = std::vector<T, std::allocator<T>>;

template <typename T,
          template <typename> class Container = SimpleVec,
          class String = std::string>
class Iterable
{
        template <typename friendT,
                  template <typename> class friendContainer,
                  class friendString>
        friend boost::indirect_iterator<typename friendContainer<std::unique_ptr<friendT>>::iterator>
            begin(Iterable<friendT, friendContainer, friendString>& i);

        template <typename friendT,
                  template <typename> class friendContainer,
                  class friendString>
        friend boost::indirect_iterator<typename friendContainer<std::unique_ptr<friendT>>::iterator>
            end(Iterable<friendT, friendContainer, friendString>& i);
};

And the free functions :

template <typename T,
          template <typename> class Container = SimpleVec,
          class String = std::string>
boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>
    begin(Iterable<T, Container, String>& i)
{
    return boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>(begin(i._c));
}

template <typename T,
          template <typename> class Container = SimpleVec,
          class String = std::string>
boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>
    end(Iterable<T, Container, String>& i)
{
    return boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>(end(i._c));
}

On g++, the error is :

../../API/net/session/ClientSession.h:83:29: error: call of overloaded 'begin(GroupManager&)' is ambiguous
      for(auto& grp : groups())
                             ^
../../API/net/session/ClientSession.h:83:29: note: candidates are:
In file included from ../../API/net/session/../permission/full/PermissionManager.h:5:0,
                 from ../../API/net/session/Session.h:3,
                 from ../../API/net/session/ClientSession.h:2,
                 from ../../API/net/session/ClientSessionBuilder.h:2,
                 from ../client/main.cpp:2:
../../API/net/session/../permission/full/../../Iterable.h:22:4: note: boost::indirect_iterator<typename friendContainer<std::unique_ptr<friendT> >::iterator> begin(Iterable<friendT, friendContainer, friendString>&) [with friendT = Group; friendContainer = SimpleVec; friendString = std::basic_string<char>; T = Permission; Container = SimpleVec; String = std::basic_string<char>; typename friendContainer<std::unique_ptr<friendT> >::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<Group, std::default_delete<Group> >*, std::vector<std::unique_ptr<Group, std::default_delete<Group> >, std::allocator<std::unique_ptr<Group, std::default_delete<Group> > > > >]
    begin(Iterable<friendT, friendContainer, friendString>& i);
    ^
../../API/net/session/../permission/full/../../Iterable.h:142:2: note: boost::indirect_iterator<typename Container<std::unique_ptr<_Tp> >::iterator> begin(Iterable<T, Container, String>&) [with T = Group; Container = SimpleVec; String = std::basic_string<char>; typename Container<std::unique_ptr<_Tp> >::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<Group, std::default_delete<Group> >*, std::vector<std::unique_ptr<Group, std::default_delete<Group> >, std::allocator<std::unique_ptr<Group, std::default_delete<Group> > > > >]
  begin(Iterable<T, Container, String>& i)
  ^

So it looks like g++ sees the friend declaration as another function ?

Jean-Michaël Celerier
  • 7,412
  • 3
  • 54
  • 75

2 Answers2

4

I finally managed to find an answer (which is much more readable) thanks to @sehe's answer and this cppreference page.

template <typename T,
          template <typename> class Container = SimpleVec,
          class String = std::string>
class Iterable
{
        friend boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>
            begin(Iterable& i)
        {
            return boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>(begin(i._c));
        }

        friend boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>
            end(Iterable& i)
        {
            return boost::indirect_iterator<typename Container<std::unique_ptr<T>>::iterator>(end(i._c));
        }
};
Jean-Michaël Celerier
  • 7,412
  • 3
  • 54
  • 75
3

An in-class friend function acts as a free function declared in the containing scope.

It sees the friend declaration as another function because it is (in fact, another function template generating unbounded functions, all different from the free function).

Had the templates been identical, you would get a different diagnostic:

main.cpp:13:13: error: redefinition of 'begin'
char const* begin(X const&)   { return data; }
            ^
main.cpp:9:24: note: previous definition is here
    friend char const* begin(X const&)   { return data; }
                       ^

See it Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • @Yakk oops. Fixed. The message is much more descriptive now too. Although, indeed, the OP's problem was more with template functions rather than in-class definition of free functions, possibly. My +1 is with the other answer – sehe Oct 20 '14 at 19:12
  • the next question is, how are the OP's two versions non-identical? And the OP's `friend` had no body, your does. – Yakk - Adam Nevraumont Oct 20 '14 at 19:51
  • @Yakk. Sigh. [I messed this up](http://coliru.stacked-crooked.com/a/606a4d268719ca09), didn't I :| You're right. Except for suggesting inline definitions I didn't explain any of the mystery. Will delete in the morning. Cheers – sehe Oct 20 '14 at 21:24