0

When creating a simple iterator adapter I received a compiler error: "'IteratorAdapter getAdapter(const ContainerT::iterator &)' : could not deduce template argument for 'ContainerT'". Here is the code:

#include <list>

template <class ContainerT>
class IteratorAdapter
{
public:
    IteratorAdapter(const typename ContainerT::iterator& it) : 
        it_(it) {}
private:
    typename ContainerT::iterator it_;
};

template <class ContainerT>
IteratorAdapter<ContainerT> getAdapter(
   const typename ContainerT::iterator& it)
{
    return IteratorAdapter<ContainerT>(it);
}

template <class IteratorT>
void someFunc(IteratorT beg, IteratorT end)
{
   // ...
}

int main(int argc, char **argv)
{
    std::list<int> s;
    someFunc(getAdapter(s.begin()), getAdapter(s.end()));
    return 0;
}

I thought that it might be related to the ambiguity between const and non-const begin() and end(). So I added a const reference to list, but unfortunately the result was the same. Why is compiler generating this error? How can it be fixed?

bkxp
  • 1,115
  • 1
  • 12
  • 20
  • There's no direct fix, you can't deduce a type from its nested type. Imagine the situation where two different container types both use a `char*` as an iterator. Which one should the compiler choose? – jrok Jan 13 '14 at 12:55
  • Since nested types are uniquely defined within their scope and can't be mixed up with other types (unlike typedefs), why can't the compiler use this information to match a template declaration? There must be a serious reason not to allow it. – bkxp Jan 13 '14 at 13:03
  • I should've said "from a nested type name". `iterator` can be either a typedef or a concrete type, compiler can't know that from declaration. – jrok Jan 13 '14 at 13:11

2 Answers2

0

You don't need the container argument in the IteratorAdapter template. Use

template <class IteratorT>
class IteratorAdapter
{
public:
    IteratorAdapter(const IteratorT& it) : 
     it_(it) {}
private:
     IteratorT it_;
};

template <class IteratorT>
IteratorAdapter<IteratorT> getAdapter(
    const IteratorT& it)
{
    return IteratorAdapter<IteratorT>(it);
}
Wojtek Surowka
  • 20,535
  • 4
  • 44
  • 51
  • This is just a generic example of the problem. In some cases you need to know the exact type of container in the iterator adapter. – bkxp Jan 13 '14 at 13:01
  • Then you may use some kind of traits mechanism, in the simplest case it would be template struct IteratorTraits { }; template<> struct IteratorTraits::iterator> { typedef std::list ContainerT; }; template class IteratorAdapter { public: IteratorAdapter(const IteratorT& it) : it_(it) {} private: typedef typename IteratorTraits::ContainerT ContainerT; typename ContainerT::iterator it_; }; – Wojtek Surowka Jan 13 '14 at 13:14
  • Then this is the approach that possibly works in boost::make_indirect_iterator(). Otherwise I can't understand how can it work with only one template parameter. – bkxp Jan 13 '14 at 13:19
0

As mentioned, the type cannot be deduced in getAdapter. (it may have several correct type).

So you may call explicitly:

someFunc(getAdapter<std::list<int> >(s.begin()), getAdapter<std::list<int> >(s.end()));

or create functions which take the container (so deduction is possible).

template <class ContainerT>
IteratorAdapter<ContainerT> getAdapterBegin(const ContainerT& container)
{
    return IteratorAdapter<ContainerT>(container.begin());
}
template <class ContainerT>
IteratorAdapter<ContainerT> getAdapterEndconst ContainerT& container)
{
    return IteratorAdapter<ContainerT>(container.end());
}

A possible hack:

template <class ContainerT>
IteratorAdapter<ContainerT> getAdapter(const ContainerT&,
                                       const typename ContainerT::iterator& it)
{
    return IteratorAdapter<ContainerT>(it);
}

// call it like: getAdapter(s, s.begin())
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thank you for the code examples. BTW I found a mentioning of boost::make_indirect_iterator() in a related topic, and now I'm trying to figure out how they manage to do it with only one template argument. Probably they have predefined 'traits' for all std containers, so if I want to get an iterator adapter for a custom container, then I'll probably have to implement my own traits class. Looks like there is no other way to do it. – bkxp Jan 13 '14 at 13:21