函数模板重载决议,依赖和非依赖参数
Function template overload resolution, dependent and non-dependent parameters
给定以下程序
#include <iostream>
template<class T> struct id { using type = T; };
template<class T1, class T2>
int func(T1, T2) { return 0; }
template<class T1, class T2>
int func(typename id<T1>::type, typename id<T2>::type) { return 1; }
int main()
{
std::cout << func<int, int>(0, 0) << std::endl;
}
GCC 和 Clang 都为该程序打印 1
。这个程序保证按标准打印1
吗?
我试图找到答案 here 但无法破译。看起来函数模板可能是等效的,因此会破坏 ODR,但我不确定。
是否将第二个功能模板更改为
template<class T>
using id_type = typename id<T>::type;
template<class T1, class T2>
int func(id_type<T1>, id_type<T2>) { return 1; }
有所作为?
以下func
重载
// Denote as overload F.
template<class T1, class T2>
int func(typename id<T1>::type, typename id<T2>::type) { return 1; }
比以下func
重载
更专业
// Denote as overload G.
template<class T1, class T2>
int func(T1, T2) { return 0; }
因此,重载决策选择了前者。
(以下所有 ISO 标准参考均参考 N4659: March 2017 post-Kona working draft/C++17 DIS)
func
的 G
和 F
重载的部分排序受以下因素支配:
这是沼泽标准偏序。我们将唯一类型替换为一个函数模板,并尝试根据它推导另一个。双向进行,如果演绎只在一个方向上成功,我们就有一个命令。如果您想阅读奥术规则,请参阅 [temp.func.order] 和 [temp.deduct.partial]。
所以在这里,
- 将
T1=U1, T2=U2
代入第一个重载的函数类型会产生 int f(U1, U2);
我们可以从中推导出第二个重载中的 T1
和 T2
吗?不;两者都在非推导的上下文中。因此,演绎失败。
- 将
T1=U1, T2=U2
代入第二个重载会产生 int f(id<U1>::type, id<U2>::type)
(这是在定义上下文中进行的,因此我们不能进一步代入 id
- 某处可能有专门化) .我们可以从中推导出第一个重载中的 T1
和 T2
吗?是的,通过推导 T1 = id<U1>::type
和 T2 = id<U2>::type
。扣分成功
由于推导仅在一个方向上成功 - 从转换后的第二个方向推导第一个 - 第二个比第一个更专业,并且优先由重载决议选择。
别名模板大小写没有任何变化。
这些模板既不等价也不在功能上等价。
给定以下程序
#include <iostream>
template<class T> struct id { using type = T; };
template<class T1, class T2>
int func(T1, T2) { return 0; }
template<class T1, class T2>
int func(typename id<T1>::type, typename id<T2>::type) { return 1; }
int main()
{
std::cout << func<int, int>(0, 0) << std::endl;
}
GCC 和 Clang 都为该程序打印 1
。这个程序保证按标准打印1
吗?
我试图找到答案 here 但无法破译。看起来函数模板可能是等效的,因此会破坏 ODR,但我不确定。
是否将第二个功能模板更改为
template<class T>
using id_type = typename id<T>::type;
template<class T1, class T2>
int func(id_type<T1>, id_type<T2>) { return 1; }
有所作为?
以下func
重载
// Denote as overload F. template<class T1, class T2> int func(typename id<T1>::type, typename id<T2>::type) { return 1; }
比以下func
重载
// Denote as overload G. template<class T1, class T2> int func(T1, T2) { return 0; }
因此,重载决策选择了前者。
(以下所有 ISO 标准参考均参考 N4659: March 2017 post-Kona working draft/C++17 DIS)
func
的 G
和 F
重载的部分排序受以下因素支配:
这是沼泽标准偏序。我们将唯一类型替换为一个函数模板,并尝试根据它推导另一个。双向进行,如果演绎只在一个方向上成功,我们就有一个命令。如果您想阅读奥术规则,请参阅 [temp.func.order] 和 [temp.deduct.partial]。
所以在这里,
- 将
T1=U1, T2=U2
代入第一个重载的函数类型会产生int f(U1, U2);
我们可以从中推导出第二个重载中的T1
和T2
吗?不;两者都在非推导的上下文中。因此,演绎失败。 - 将
T1=U1, T2=U2
代入第二个重载会产生int f(id<U1>::type, id<U2>::type)
(这是在定义上下文中进行的,因此我们不能进一步代入id
- 某处可能有专门化) .我们可以从中推导出第一个重载中的T1
和T2
吗?是的,通过推导T1 = id<U1>::type
和T2 = id<U2>::type
。扣分成功
由于推导仅在一个方向上成功 - 从转换后的第二个方向推导第一个 - 第二个比第一个更专业,并且优先由重载决议选择。
别名模板大小写没有任何变化。
这些模板既不等价也不在功能上等价。