包含模板参数包的函数模板的部分排序
Partial ordering of function templates containing template parameter packs
Partial ordering of function templates containing template parameter packs is independent of the number of deduced arguments for those template parameter packs.
template<class...> struct Tuple { };
template< class... Types> void g(Tuple<Types ...>); // #1
template<class T1, class... Types> void g(Tuple<T1, Types ...>); // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
以上内容转自partial ordering of overload function templates。我不太明白为什么g(Tuple<int>()); // calls #3
。具体来说,为什么不能调用#2
?以下是我的推理,有错误请指出:
注意:我会忽略#1
b/c解释得很好
第 1 步:推导和替换以及重载决议得出这些:
- void(Tuple
) [T1 = int,类型为空]#2
- void(Tuple< int>) [T1 = int, Types& 为空] #3
步骤 2:转换两个函数模板:
- void g(元组< C1, Pack1...>);
- void g(Tuple< C2, Pack2&...>);
Step3:这是一个函数调用上下文,类型是函数调用有参数的那些函数参数类型:
- 从 Tuple
推导 Tuple OK [T1 = C2;类型... = 包装&...]
- 从 Tuple
推导出 Tuple) 好吗? [T1 = C1; Types&... 呢?可以从Pack1推导出来吗...?此处引用被删除了吗?]
3) If P is a reference type, the type referred to by P is used for deduction.This seems fine.
If P has one of the forms that include a template parameter list < T> or < I>, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.
Step4:如果最后一步正确。那么这意味着#3 并不比#2 更专业。所以对于应该解析到哪个函数模板是模棱两可的。
更新:我想我误解了上面的相关引述。当我们将 P 中的模板参数与 A 中的模板参数进行匹配时,它们是逐字匹配的,这意味着在 函数调用 参数和参数上完成的所有转换和分析都不会再次应用我们在 P/A 中匹配模板 parameters/arguments(实际上是 函数调用 parameter/arguments)。然后它将在上面的 步骤 3 中从 Tuple< C1, Pack1...>)
推导出 Tuple< T1, Types&...>
失败。所以 #3
更专业。
重载决议的第一步是找到所有候选者。对于调用 g(Tuple<int>())
,存在三个可行的候选者:
g(Tuple<int>); // #1: [Types = {int}]
g(Tuple<int>); // #2: [T1 = int, Types = {}]
g(Tuple<int>); // #3: [T1 = int, Types = {}]
从转换序列的角度来看,这三个都是同样可行的候选者(因为,当然,它们都采用相同的参数,即输入的完全匹配)。它们都是函数模板特化,所以我们也不能以此为基础进行区分。
所以我们只剩下函数模板偏序了。我们为每个重载合成类型,并尝试使用我们合成的类型针对每个其他重载执行模板推导。更简单的比较是 (1) vs (2) 和 (1) vs (3)。在那里,我们有 [temp.deduct.partial]:
If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails.
由于我们将Types...
转化为UniqA...
,并且第一个参数是T1
,类型推导失败。这使得 (2) 和 (3) 都比 (1) 更专业。所以现在我们比较(2)和(3)。
首先,我们可以从(3)推导出(2)吗?
template <class T1, class... Types> void g(Tuple<T1, Types...> );
g(Tuple<Uniq3, Uniq3Pack&...>());
当然,没问题 T1 == Uniq3
和 Types... == Uniq3Pack&...
。接下来,我们尝试另一个方向:
template <class T1, class... Types> void g(Tuple<T1, Types&...> );
g(Tuple<Uniq2, Uniq2Pack...>());
这失败了,因为 Uniq2Pack
不是一组引用类型而 Types&...
是。由于演绎只在一个方向上成功,这使得 (3) 成为更专业的重载。
由于它是最后一名,(3) 是最可行的候选者。这似乎违反直觉,因为我们实际上并没有使用引用类型调用它 - 但它仍然是最佳选择。
Partial ordering of function templates containing template parameter packs is independent of the number of deduced arguments for those template parameter packs.
template<class...> struct Tuple { };
template< class... Types> void g(Tuple<Types ...>); // #1
template<class T1, class... Types> void g(Tuple<T1, Types ...>); // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
以上内容转自partial ordering of overload function templates。我不太明白为什么g(Tuple<int>()); // calls #3
。具体来说,为什么不能调用#2
?以下是我的推理,有错误请指出:
注意:我会忽略#1
b/c解释得很好
第 1 步:推导和替换以及重载决议得出这些:
- void(Tuple
) [T1 = int,类型为空]#2 - void(Tuple< int>) [T1 = int, Types& 为空] #3
步骤 2:转换两个函数模板:
- void g(元组< C1, Pack1...>);
- void g(Tuple< C2, Pack2&...>);
Step3:这是一个函数调用上下文,类型是函数调用有参数的那些函数参数类型:
- 从 Tuple
推导 Tuple OK [T1 = C2;类型... = 包装&...] - 从 Tuple
推导出 Tuple ) 好吗? [T1 = C1; Types&... 呢?可以从Pack1推导出来吗...?此处引用被删除了吗?]
3) If P is a reference type, the type referred to by P is used for deduction.This seems fine.
If P has one of the forms that include a template parameter list < T> or < I>, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.
Step4:如果最后一步正确。那么这意味着#3 并不比#2 更专业。所以对于应该解析到哪个函数模板是模棱两可的。
更新:我想我误解了上面的相关引述。当我们将 P 中的模板参数与 A 中的模板参数进行匹配时,它们是逐字匹配的,这意味着在 函数调用 参数和参数上完成的所有转换和分析都不会再次应用我们在 P/A 中匹配模板 parameters/arguments(实际上是 函数调用 parameter/arguments)。然后它将在上面的 步骤 3 中从 Tuple< C1, Pack1...>)
推导出 Tuple< T1, Types&...>
失败。所以 #3
更专业。
重载决议的第一步是找到所有候选者。对于调用 g(Tuple<int>())
,存在三个可行的候选者:
g(Tuple<int>); // #1: [Types = {int}]
g(Tuple<int>); // #2: [T1 = int, Types = {}]
g(Tuple<int>); // #3: [T1 = int, Types = {}]
从转换序列的角度来看,这三个都是同样可行的候选者(因为,当然,它们都采用相同的参数,即输入的完全匹配)。它们都是函数模板特化,所以我们也不能以此为基础进行区分。
所以我们只剩下函数模板偏序了。我们为每个重载合成类型,并尝试使用我们合成的类型针对每个其他重载执行模板推导。更简单的比较是 (1) vs (2) 和 (1) vs (3)。在那里,我们有 [temp.deduct.partial]:
If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails.
由于我们将Types...
转化为UniqA...
,并且第一个参数是T1
,类型推导失败。这使得 (2) 和 (3) 都比 (1) 更专业。所以现在我们比较(2)和(3)。
首先,我们可以从(3)推导出(2)吗?
template <class T1, class... Types> void g(Tuple<T1, Types...> );
g(Tuple<Uniq3, Uniq3Pack&...>());
当然,没问题 T1 == Uniq3
和 Types... == Uniq3Pack&...
。接下来,我们尝试另一个方向:
template <class T1, class... Types> void g(Tuple<T1, Types&...> );
g(Tuple<Uniq2, Uniq2Pack...>());
这失败了,因为 Uniq2Pack
不是一组引用类型而 Types&...
是。由于演绎只在一个方向上成功,这使得 (3) 成为更专业的重载。
由于它是最后一名,(3) 是最可行的候选者。这似乎违反直觉,因为我们实际上并没有使用引用类型调用它 - 但它仍然是最佳选择。