begin() and end() free function overload on template
Asked Answered
G

2

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 ?

Gombroon answered 20/10, 2014 at 10:19 Comment(0)
G
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));
        }
};
Gombroon answered 20/10, 2014 at 12:32 Comment(0)
C
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

Culhert answered 20/10, 2014 at 11:24 Comment(3)
@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 answerCulhert
the next question is, how are the OP's two versions non-identical? And the OP's friend had no body, your does.Mardellmarden
@Yakk. Sigh. I messed this up, didn't I :| You're right. Except for suggesting inline definitions I didn't explain any of the mystery. Will delete in the morning. CheersCulhert

© 2022 - 2024 — McMap. All rights reserved.