3
template<typename T>
Ref<Iterator<T> > GetFilterIterator(Ref<Iterator<T> > i, boost::function<bool(T)> pred) {
    return new FilterIterator<T>(i, pred);
}


Ref<Iterator<CWorm*> > x = GetFilterIterator(worms(), &CWorm::getLocal);

And worms() returns a Ref<Iterator<CWorm*> Ref> and there is bool CWorm::getLocal(); (which is a member function). And:

template<typename T> struct Ref {
     // ...
};

template<typename T> struct Iterator {
     // ...
};

This will fail to deduce the template argument:

Iter.h:272:27: note: candidate template ignored: failed template argument deduction [3]

Why?

If I call it with the specified template argument, i.e. GetFilterIterator<CWorm*>(worms(), &CWorm::getLocal), it doesn't complain. I wonder why it cannot deduce the template argument like this. And can I make it different somehow so that it would be able to automatically deduce the type?

Albert
  • 65,406
  • 61
  • 242
  • 386

3 Answers3

2

Do you mean typname Iterator<T>::Ref for the type of the first parameter in the GetFilterIterator template declaration? If so, that is not a deducible context for template type parameters.

Consider:

template<>
struct Iterator<Foo> {
    typedef int Ref;
};
template<>
struct Iterator<Bar> {
    typedef int Ref;
};

GetFilterIterator(int(0),f);

Both Iterator<Foo>::Ref and Iterator<Bar>::Ref match the parameter passed to GetFilterIterator, an int. Which one should it pick? C++ disallows deducing template types from parameters like the one you've declared.


With the update to your question it looks like you do mean ::Ref<Iterator<T> >. I think that should be deducible then, and since the typedef Iterator<CWorm*>::Ref is ::Ref<Iterator<CWorm*> > it seems like it should be able to deduce T. I'm not sure why it's not working.

bames53
  • 86,085
  • 15
  • 179
  • 244
1

The compiler cannot deduce the template arguments because fitting to the parameters would mean a non-trivial conversion - first to Iterator<T> and then to Ref<Iterator<T> > which both require user-defined conversions. Also, directly converting the member function pointer to boost::function is similarly non-trivial for the compiler.

IBM has a list of supported template parameter deductions.

If you want your template arguments to be deduced automatically, you have to provide wrapper methods:

template <typename T>
Ref<Iterator<T> > makeIteratorRef(T val)  {
    return Ref<Iterator<T> >(Iterator<T>(val));
}

template <typename T>
boost::function<bool (T)> makeFn(bool (T::*fn) () const)  {
    boost::function<bool (T)> res = boost::bind(fn, _1);    
    return res;
}

...

Ref<Iterator<CWorm*> > x = GetFilterIterator(makeIteratorRef(worms()), makeFn(&CWorm::getLocal));

This way the compiler is capable of deducing the template parameters because no conversions are necessary.

By the way, I think you are overcomplicating simple things:

for (auto it = worms().begin(); it != worms().end(); ++it)
  if (it->isLocal()) { 
    // Do something
  }

This code is way more readable in C++ and even though it might not be as general it hardly makes the code worse.

Karel Petranek
  • 15,005
  • 4
  • 44
  • 68
  • There is no conversion involved. It just needs to match `Ref >` with `Ref >`. I always thought that wouldn't be a problem (and I also don't see why it is; I'm quite sure I have seen similar stuff elsewhere). Also, this already determines `T`, so the `boost::function` stuff doesn't matter anymore then. – Albert Jan 13 '12 at 01:22
  • Ah, did you overread this? "`worms()` returns a `Ref Ref>`". – Albert Jan 13 '12 at 01:36
  • Ah, sorry, missed that point. Then the problem lies in conversion to boost::function - you will need the makeFn wrapper or some alternative to it. – Karel Petranek Jan 13 '12 at 07:51
0

Thanks to the hint from Xeo to here about that implicit type conversions are not allowed when deducing template arguments, I wondered wether the second parameter might cause problems here. I thought that it would do the type deduction from left to right and once the type is deducted, it is not a problem anymore (for the function pointer to boost::function cast).

It seems I was wrong and this was exactly the problem.

Another version of the same thing avoids the problem:

template<typename T>
struct PartialFuncWrapper {
    ::Ref<Iterator<T> > i;
    PartialFuncWrapper(::Ref<Iterator<T> > _i) : i(_i) {}
    typename Iterator<T>::Ref operator()(boost::function<bool(T)> pred) {
        return new FilterIterator<T>(i, pred);      
    }
};

template<typename T>
PartialFuncWrapper<T> GetFilterIterator(::Ref<Iterator<T> > i) {
    return PartialFuncWrapper<T>(i);
}

Then I can write:

Ref<Iterator<CWorm*> > x = GetFilterIterator(worms())(&CWorm::getLocal);
Community
  • 1
  • 1
Albert
  • 65,406
  • 61
  • 242
  • 386