C ++模板参数推断失败。为什么?

时间:2012-01-10 19:45:22

标签: c++ templates

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

并且worms()返回Ref<Iterator<CWorm*> Ref>并且bool CWorm::getLocal();(这是一个成员函数)。和

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

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

这将无法推断出模板参数:

  

Iter.h:272:27:注意:候选模板被忽略:模板参数演绎失败[3]

为什么?

如果我用指定的模板参数调用它,即GetFilterIterator<CWorm*>(worms(), &CWorm::getLocal),它就不会抱怨。我想知道为什么它不能像这样推断出模板参数。我可以以某种方式使它不同,以便它能够自动推断出类型吗?

3 个答案:

答案 0 :(得分:2)

对于typname Iterator<T>::Ref模板声明中第一个参数的类型,您的意思是GetFilterIterator吗?如果是这样,那不是模板类型参数的可推导上下文。

考虑:

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

GetFilterIterator(int(0),f);

Iterator<Foo>::RefIterator<Bar>::Ref都匹配传递给GetFilterIterator的参数,即int。它应该选哪一个? C ++不允许从您声明的参数中推导出模板类型。


通过更新您的问题,您的意思是::Ref<Iterator<T> >。我认为那应该是可以推断的,因为typedef Iterator<CWorm*>::Ref::Ref<Iterator<CWorm*> >,它似乎应该能够推导出T。我不确定为什么它不起作用。

答案 1 :(得分:1)

编译器无法推导出模板参数,因为拟合参数意味着进行非平凡的转换 - 首先是Iterator<T>,然后是Ref<Iterator<T> >,这两个转换都需要用户定义的转换。此外,直接将成员函数指针转换为boost :: function对于编译器来说同样重要。

IBM有一个list of supported template parameter deductions

如果希望自动推导出模板参数,则必须提供包装器方法:

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

这样编译器就能够推导出模板参数,因为不需要转换。

顺便说一句,我认为你过于复杂的简单事情:

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

这段代码在C ++中更具可读性,即使它可能不那么通用,也几乎不会使代码变得更糟。

答案 2 :(得分:0)

由于从Xeo到here的提示有关在推导模板参数时不允许隐式类型转换,我想知道第二个参数可能会在这里引起问题。我认为它会从左到右进行类型推导,一旦类型被推导,它就不再是问题了(对于指向boost::function强制转换的函数指针)。

看来我错了,这正是问题所在。

同一件事的另一个版本避免了这个问题:

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

然后我可以写:

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