c++中重载递归函数的模板推导规则

rule for template deduction for overloaded recursive function in c++

本文关键字:规则 重载 递归函数 c++      更新时间:2023-10-16

我发现了很多关于模板推导的信息(例如C++模板函数重载规则(,但这并不能帮助我理解重载递归函数的模板推导行为。在下面的代码中,我真的不明白编译器是如何推断出它应该为vectvect使用vector<T>函数两次,为pairpair使用pair<T,U>函数两次的——但它可以。因此,我不明白为什么它不能推断出vectpair应该同时使用vector<T>pair<T,U>函数?

这与为什么const关键字会增加转换从而使T功能更好有关吗?(但是在这种情况下,另外两个例子是如何工作的呢?(

是否只有在递归调用中首先测试当前函数的模板推导时,才有可能进行两个第一次推导?

#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
template<class T> string print(const T& t){
    ostringstream s;
    s << t;
    return s.str();
}
template<class T> string print(const vector<T>& t){
    ostringstream s;
    s << '[';
    for(int i=0;i<(int)t.size();i++)
        s << print(t[i]) << ' ';
    s << ']';
    return s.str();
}
template<class T,class U> string print(const pair<T,U>& t){
    ostringstream s;
    s << '(' << print(t.first) << ',' << print(t.second) << ')';
    return s.str();
}
int main ( int argc, char **argv ) {
    vector<vector<double> > vectvect(4,vector<double>(4));
    for(int i=0;i<(int)4;i++)
        for(int j=0;j<(int)4;j++)
            vectvect[i][j] = i*4+j;
    pair<int,pair<string,double> > pairpair = make_pair(10, make_pair("foo",0.5));
    vector<pair<int,string> > vectpair(1,make_pair(42,"bar"));
    ///template deduction
    cout << print(vectvect) << endl;
    cout << print(pairpair) << endl;
    ///template deduction failure
    //====> here is the problem 
    //cout << print(vectpair) << endl;
    return 0;
}

目前,我只是想了解,但如果有人知道如何在不引入大量源代码开销的情况下做到这一点,我很感兴趣。

谢谢。

问题与模板参数推导无关,也与重载解决无关。采用一对的print重载不会被编译器选中,因为它不能通过非限定名称查找找到,ADL也不能找到。您应该对函数的两个定义进行重新排序,以便取一对的定义优先:

template <class T,class U> string print(const pair<T,U>& t){
    /**/
}
template <class T> string print(const vector<T>& t){
    /**/
}

或者在定义和使用之前声明所有函数:

template <class T> string print(const T& t);
template <class T,class U> string print(const pair<T,U>& t);    
template <class T> string print(const vector<T>& t);

您的问题是,当您试图打印vector<pair<X>>时,这个带有vector重载的调用:

s << print(t[i]) << ' ';

找不到pair<T,U>重载,因为不合格查找发生在定义点,而不是以后。因此,它调用您的通用print(const T&)并不是因为模板排序规则中的一些中断,而是因为pair<T, U>重载根本不可见。


如果我能用Leeroy Jenkins的声音说Argument Dependent Lookup就好了。那太棒了。不管怎样,ADL最酷的地方是查找发生在之后。因此,您不必担心确保所有功能都是预定义的。只要多加一个论点,让ADL来做它的事情。在顶层,我们只是向前推进:

template <typename T>
string print(T const& val)
{
    return print(adl::helper{}, val);
}

然后我们在namespace adl:中定义了所有相同的print函数

namespace adl {
    struct helper{};
    template<class A, class T> string print(A, const T& t){
        ostringstream s;
        s << t;
        return s.str();
    }
    template<class A, class T> string print(A, const vector<T>& t){
        ostringstream s;
        s << '[';
        for(int i=0;i<(int)t.size();i++)
            s << print(helper{}, t[i]) << ' ';
        s << ']';
        return s.str();
    }
    template<class A, class T,class U> string print(A, const pair<T,U>& t){
        ostringstream s;
        s << '(' << print(helper{}, t.first) << ',' << print(helper{}, t.second) << ')';
        return s.str();
    }
}

任何地方都没有订购问题。现在我们甚至可以打印…的pairvectorpairvector。。。