#include <iostream>
#include <type_traits>
template<typename T,
typename = typename std::enable_if<std::is_pod<T>::value>::type
>
T foo(T t) { return t; }
template<typename T>
void foo(T t) { }
int main()
{
foo<int>(5);
}
main.cpp: In function 'int main()':
main.cpp:14:15: error: call of overloaded 'foo(int)' is ambiguous
foo<int>(5);
^
main.cpp:14:15: note: candidates are:
main.cpp:7:3: note: T foo(T) [with T = int; <template-parameter-1-2> = void]
T foo(T t) { return t; }
^
main.cpp:10:6: note: void foo(T) [with T = int]
void foo(T t) { }
^
是否可以解决代码中的错误?
答案 0 :(得分:14)
使用SFINAE约束函数不会引入排序,它只是意味着约束函数可调用或不可调用,因此当启用第一个重载时,您有两个不明确的函数。
最简单的解决方案是使用相同约束的反转来禁用第二个重载,以便只有一个适用于给定类型。要完成这项工作,您需要将enable_if
移动到返回类型,而不是默认模板参数(因为默认模板参数不是函数签名的一部分,所以不能用于重载,仅限制SFINAE)。除非你能为每个重载提出一个单独的谓词(即类型特征的组合),使得没有任何参数类型匹配多个谓词,否则这个解决方案不会对大量重载起作用。这意味着谓词必须是互斥的,你不能让谓词测试一个概念,比如 signed integral type ,这是另一个测试概念的更精炼版本,例如整数类型,因为两个谓词都适用于int
,并且将启用多个函数。
C ++ Concepts比SFINAE更好的解决方案的一个重要原因是受概念约束的函数是有序的,因此更受约束的函数比较少约束的函数更好地匹配。这意味着你不需要玩这些SFINAE游戏并且有相互排斥的约束,编译器会做正确的事。
答案 1 :(得分:6)
将相同的SFINAE应用于备用方法,只需将其反转(并将其移至返回类型);即禁用另一个。
template<typename T>
typename std::enable_if<std::is_pod<T>::value, T>::type
foo(T t) { return t; }
template<typename T>
typename std::enable_if<!std::is_pod<T>::value, void>::type
foo(T t) { }
你提到会有很多超载,所以这可能很麻烦......你的里程可能会有所不同。
注意,为什么只有2?最初的问题是2,但这里的问题更多是涉及模板约束的许多重载之间的排序。当只有少量重载(具有更易于管理的SFINAE约束)时,这个答案留给更经典的解决方案。
答案 2 :(得分:4)
最简单的解决方案是在第一个约束时禁用第二个函数。
更一般地说,您可以使用重载排名来选择特定功能,该功能可以更好地扩展。它是这样的:
template<int N> struct choice : choice<N - 1> {};
template<> struct choice<0> {};
这里,编译器必须执行N个派生到基础的转换才能从N到0。所以我们可以将过载从N到0排序,其中N是最理想的,0是最不可取的。
template<typename T,
typename = typename std::enable_if<std::is_pod<T>::value>::type
>
T foo(T t, choice<1>) { return t; }
template<typename T>
void foo(T t, choice<0>) { }
int main()
{
foo<int>(5, choice<1>());
}
现在您不必执行O(n ^ 2)条件重复。
答案 3 :(得分:0)
将enable_if
移动到返回类型,并使用反向约束。
template <typename T>
auto foo(T t) -> typename std::enable_if<std::is_pod<T>::value, T>::type
{
return t;
}
template <typename T>
auto foo(T t) -> typename std::enable_if<!std::is_pod<T>::value, void>::type
{
}
int main()
{
int i = foo<int>(1);
}