重载C++模板化函数
Overloading of C++ templated functions
我认为以下代码应该可以工作,但是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;
}
该错误是由于最后一行以及第二个和第三个result
函数被认为是等效的事实造成的。虽然我认为第二个比第三个更适合(就像第一个一样)。
不过我不确定。谁能告诉我是我错了还是编译器错了?
TLDR;程序编译失败的原因是,在重载解析期间,第二个和第三个重载是同样良好的匹配。特别是,两者都不比另一个更专业。由于重载分辨率无法选择最佳匹配项,因此程序格式不正确。治愈的方法就是用你的方式摆脱它。
问题所在
14.5.6.2 函数模板的部分排序 [temp.func.order]
2 部分排序选择两个函数模板中哪个更多 通过依次转换每个模板来比另一个模板专用(请参阅 下一段)并使用 函数类型。扣除过程确定是否 模板比其他模板更专业。如果是这样,越多 专用模板是部分排序选择的模板 过程。
3 要生成转换后的模板,对于每种类型, 非类型或模板模板参数(包括模板参数) 包(14.5.3))合成唯一的类型、值或类 分别模板并将其替换为每次出现的模板 模板的函数类型中的参数。
对于所有三个重载,第一个合成参数是相等的,并且由于所有参数都是逐个考虑的,因此我们可以专注于第二个参数。
您的第一个重载将转换为以下合成的第二个参数
const A<0, typename std::tuple_element<0, Arg1>::type>&
您的第二个重载将转换为以下合成的第二个参数
const A<
std::tuple_size<Arg1>::value-1, typename
std::tuple_element<std::tuple_size<Arg1>::value-1, Arg1>::type
>&
您的第三个重载将转换为以下合成的第二个参数
const A<Arg2, typename std::tuple_element<Arg2, Arg1>::type>&
14.8.2.4 在部分排序过程中推导模板参数 [temp.deduct.partial]
很明显,第一个和第二个重载2 使用两组类型来确定部分排序。为 涉及的每个模板都是原始函数类型和 转换后的函数类型。[注:转换后的创建 类型在 14.5.6.2 中描述。— 尾注 ] 演绎过程使用 转换后的类型作为参数模板,原始类型 另一个模板作为参数模板。此过程已完成 对于部分排序比较中涉及的每种类型两次:一次 使用转换后的模板-1 作为参数模板和 模板-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;
}
活生生的例子。
在第二个重载中,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;
}
您应该用一些标记调度替换重载。
编写一个函数,然后检查第二个 arg 是否以静态方式A
is_same
元组中的第一个类型,调用另一个具有依赖于该类型的函数。 在假分支上重复最后。
helper( t, a, std::is_same<A, std::tuple_element<0, Tuple>>() );
也许有一些decay
或remove_const
在那里。
这个想法是,如果它们相同,std::is_same<X,Y>
true_type
,否则false_type
。 helper
重载 true 和 false 类型的第三个参数,为您提供编译时分支。 对最后一个类型再次重复,您就完成了。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗