通过模板函数重载以正向顺序进行模板递归(关于std::tuple上的迭代)

Template recursion in forward order (about iteration over std::tuple) by template function overloading

本文关键字:std 关于 tuple 迭代 递归 重载 函数 顺序      更新时间:2023-10-16

我使用几种方法(出于好奇)编写了在std::tuple上迭代的代码。我成功地使用了std::enable_if方法、带有模板专用化的静态类函数、模板函数重载等。对于函数重载,我写了以下内容:

template<size_t N> struct SizeT{};
template<typename Tuple>
void print(Tuple, SizeT<0>){}
template<typename Tuple, size_t N>
void print(Tuple t, SizeT<N>)
{
    print(t,SizeT<N-1>());
    std::cout << std::get<N-1>(t) << ' ';
}
template<typename Tuple>
void print(Tuple t){
    print(t, SizeT<std::tuple_size<Tuple>::value>());
    std::cout << std::endl;
}

工作不错。但我在模板递归中尝试了正向顺序:

template<size_t N> struct SizeT{};
template<typename Tuple, size_t N>
void print(Tuple t, SizeT<N>) {
    std::cout << std::get<N>(t) << ' ';
    print(t,SizeT<N+1>());
}
template<typename Tuple>
void print(Tuple, SizeT< std::tuple_size<Tuple>::value >){
    std::cout << std::endl;
}
template<typename Tuple>
void print(Tuple t) {
    print(t,SizeT<0>());
}

存在关于不明确调用的编译错误:

call of overloaded 'print(std::tuple<A<int>&, A<int>&, A<long long int>&, A<long int>&>&, forward_first::SizeT<4u>)' is ambiguous
     print(t,SizeT<N+1>());
candidates are:
void forward_first::print(Tuple, forward_first::SizeT<N>) [with Tuple = std::tuple<A<int>&, A<int>&, A<long long int>&, A<long int>&>; unsigned int N = 4u]
 void print(Tuple t, SizeT<N>)
void forward_first::print(Tuple, forward_first::SizeT<std::tuple_size<Tuple>::value>) [with Tuple = std::tuple<A<int>&, A<int>&, A<long long int>&, A<long int>&>]
 void print(Tuple, SizeT< std::tuple_size<Tuple>::value >)

但是,如果用实际常量(例如3 literal)替换std::tuple_size::value,那么它就是工作:

template<size_t N> struct SizeT{};
template<typename Tuple, size_t N>
void print(Tuple t, SizeT<N>) {
    std::cout << std::get<N>(t) << ' ';
    print(t,SizeT<N+1>());
}
template<typename Tuple>
void print(Tuple, SizeT<3>){
    std::cout << std::endl;
}
template<typename Tuple>
void print(Tuple t) {
    print(t,SizeT<0>());
}

为什么调用std::tuple_size::value不明确?为什么使用

template<typename Tuple>
void print(Tuple, SizeT< std::tuple_size<Tuple>::value >)

没有比更专业的

template<typename Tuple, size_t N>
void print(Tuple t, SizeT<N>)

问题是在std::tuple_size中有一个嵌套的value,它的行为令人惊讶。模板参数推导中的一个快速经验法则是,::之后的任何内容对编译器来说都是不透明的。这里令人惊讶的是,它不是在参数推导阶段发生的(两个重载都有自己的参数被正确推导出来),而是在重载解析阶段从另一个重载的合成参数中推导出一个重载参数。

14.8.2.4在部分排序过程中推导模板参数[temp.dexer.partial]

2两组类型用于确定偏序。对于其中涉及的每个模板都有原始函数类型和转换后的函数类型。【注:改造后的类型如14.5.6.2所述。——结束语]推导过程使用作为参数模板的转换类型和的原始类型另一个模板作为参数模板。此过程已完成偏序比较中涉及的每种类型两次:一次使用转换后的模板-1作为参数模板,并且template-2作为参数模板,并再次使用template-2作为参数模板,template-1作为参数样板

所以考虑到你的两次过载

// #1
template<typename Tuple>
void print(Tuple, SizeT< std::tuple_size<Tuple>::value >)
// #2
template<typename Tuple, size_t N>
void print(Tuple t, SizeT<N>)

很明显,第一个没有第二个模板参数可推导,因此它至少与第二个重载一样专业。问题是第二个是否可以从第一个重载的合成第二个参数推导出它的N参数。

论证推导没有发生的原因是一个非推导的上下文:

14.8.2.5从类型[temp.dexecut.type]中推导模板参数

5未推导的上下文为:

--。。。

--非类型模板参数或数组绑定,其中子表达式引用模板参数

--。。。

这意味着第二过载也至少与第一过载一样专门。因此,过载分辨率无法选择一个,并且程序格式不正确。

注意:类似的问题(仅针对N的某些值出现3个过载和歧义)出现在本问答中。