我可以消除重载模板功能与更受约束的模板功能之间的歧义吗?

时间:2014-08-28 11:48:03

标签: c++ c++11

守则

#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) { }
      ^

问题

是否可以解决代码中的错误?

4 个答案:

答案 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);
}