Pie*_*BdR 5 c++ templates overloading overload-resolution c++11
我认为以下代码应该工作,但g ++和clang ++都返回完全相同的错误(虽然Visual C++ 2012没有).
#include <iostream>
#include <tuple>
template <int N, typename T>
struct A { };
template <typename Tuple>
double result(const Tuple& t, const A<0, typename std::tuple_element<0, Tuple>::type>& a)
{
return 0;
}
template <typename Tuple>
double result(const Tuple& t, const A<std::tuple_size<Tuple>::value-1,
typename std::tuple_element<std::tuple_size<Tuple>::value-1,Tuple>::type>& a)
{
return 1;
}
template <typename Tuple, int N>
double result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
return 0.5;
}
int main()
{
auto a = std::make_tuple(0, 1, 2., 3., 4);
std::cout << result(a, A<0,int>()) << std::endl;
std::cout << result(a, A<2,double>()) << std::endl;
std::cout << result(a, A<4,int>()) << std::endl; // Fails if uncommented
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该错误是由于最后一行以及第二和第三result函数被认为是等效的.虽然我认为第二个比第三个更合适(就像第一个一样).
我不确定.任何人都可以告诉我,如果我错了或编译器是?
在第二个重载中,该std::tuple_size<Tuple>::value-1部分取决于模板参数Tuple,因此不是更好的匹配,或者在C++中,"更专业".这就是为什么它被认为与明确具有的第三个重载相等N.
只有您的第一个重载使用的常量值0不依赖于它Tuple,因此是更好的匹配.
如果您想解决问题,可以禁用第三个重载,以便它与第二个重载匹配:
template <typename Tuple, int N>
typename std::enable_if< N != std::tuple_size<Tuple>::value-1, double >::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
return 0.5;
}
Run Code Online (Sandbox Code Playgroud)
TLDR; 程序编译失败的原因是第二次和第三次重载在重载解析期间同样匹配良好.特别是,两者都不比另一个更专业.由于重载决策无法选择最佳匹配,因此程序格式错误.您可以通过SFINAE解决问题.
14.5.6.2函数模板的部分排序[temp.func.order]
2部分排序通过依次转换每个模板(参见下一段)并使用函数类型执行模板参数推导来选择两个函数模板中哪一个比另一个更专业.演绎过程确定其中一个模板是否比另一个模板更专业.如果是这样,则更专业的模板是部分排序过程选择的模板.
3要生成转换模板,对于每种类型,非类型或模板模板参数(包括其模板参数包(14.5.3))分别合成唯一类型,值或类模板,并将其替换为每次出现模板的函数类型中的参数.
对于所有三个重载,第一个合成参数是相等的,并且因为所有参数都是逐个考虑的,所以我们可以关注第二个.
您的第一个重载转换为以下合成的第二个参数
const A<0, typename std::tuple_element<0, Arg1>::type>&
Run Code Online (Sandbox Code Playgroud)
您的第二个重载转换为以下合成的第二个参数
const A<
std::tuple_size<Arg1>::value-1, typename
std::tuple_element<std::tuple_size<Arg1>::value-1, Arg1>::type
>&
Run Code Online (Sandbox Code Playgroud)
您的第三个重载转换为以下合成的第二个参数
const A<Arg2, typename std::tuple_element<Arg2, Arg1>::type>&
Run Code Online (Sandbox Code Playgroud)
14.8.2.4在部分排序期间推导模板参数[temp.deduct.partial]
2两组类型用于确定偏序.对于涉及的每个模板,都有原始函数类型和转换后的函数类型.[注意:转换类型的创建在14.5.6.2中描述. - 结束注释]演绎过程使用变换后的类型作为参数模板,将另一个模板的原始类型用作参数模板.对于部分排序比较中涉及的每种类型,此过程完成两次:一次使用转换的模板-1作为参数模板,使用template-2作为参数模板,再次使用转换的模板-2作为参数模板和模板-1作为参数模板.
很明显,第一个和第二个重载没有第二个模板参数来推断,因此它们至少与第三个重载一样专用.问题是第三个是否可以N从第一个和第二个重载的合成第二个参数推导出它的参数.
对于第一次重载,这是真的N=0,因此第一次重载比第三次更专业.这就是您的第一个函数调用选择第一个重载的原因.
对于第三个重载,参数推导不会发生,它是一个非推导的上下文:
14.8.2.5从类型[temp.deduct.type]中推导出模板参数
5未推断的背景是:
- ......
- 非类型模板参数或数组绑定,其中子表达式引用模板参数.
- ......
这意味着第三次重载也至少与第二次过载一样专门.因此,重载决策无法选择一个,并且程序格式错误.
只需在enable_if(使用SFINAE)内部进行两次非重叠条件的重载.在这种情况下,这会绕过重载分辨率.
template <typename Tuple, int N>
typename std::enable_if<N == std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
return 1;
}
template <typename Tuple, int N>
typename std::enable_if<N != std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
return 0.5;
}
Run Code Online (Sandbox Code Playgroud)
实例.