3
votes
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?

3
What type is Iterator<CWorm*>::Ref? - sth
bool CWorm::getLocal(); cannot match boost::function<bool(T)>, because it does not take an argument. - Björn Pollex
As a member function getLocal would work as function<bool(CWorm*)> - bames53
@bames53: Only if it is wrapped using std::mem_fun. - Björn Pollex

3 Answers

2
votes

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.

1
votes

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.

0
votes

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);