2014-01-10 104 views
5

在下面的程序中,尽管我有一个enable_if语句将函数限制为整型容器类型,但这两个函数调用都会打印“非整型超载”。这是为什么?为什么重载分辨率选择第一个函数?

#include <iostream> 
#include <vector> 
#include <type_traits> 

template<bool B, typename V = void> 
using enable_if = typename std::enable_if<B, V>::type; 

template<typename ForwardIt> 
auto f(ForwardIt first, ForwardIt) 
    -> enable_if<std::is_integral<decltype(*first)>{}> 
{ 
    std::cout << "Integral container type" << std::endl; 
} 

template<typename ForwardIt> 
void f(ForwardIt, ForwardIt) 
{ 
    std::cout << "Non-integral container type" << std::endl; 
} 

int main() 
{ 
    struct X { }; 

    std::vector<int> iv; 
    std::vector<X> xv; 

    f(iv.begin(), iv.end()); // "Non-integral container type" 
    f(xv.begin(), xv.end()); // "Non-integral container type" 
} 

我甚至试过在第二次过载时使用enable_if<!std::is_integral<...>>但无济于事。

回答

6

对于迭代器类型foo,decltype(*foo)将是foo::value_type&。引用类型绝对不是完整的。您需要用std::is_integral特质,这是很容易与std::decay改造型特征进行评估类型之前删除引用(以及可能的CV-资格以及,IIRC):

template<bool B, typename V = void> 
using enable_if = typename std::enable_if<B, V>::type; 

template<typename T> 
using decay = typename std::decay<T>::type; 

template<typename ForwardIt> 
auto f(ForwardIt first, ForwardIt) 
    -> enable_if<std::is_integral<decay<decltype(*first)>>{}> 
{ 
    std::cout << "Integral container type" << std::endl; 
} 

这将导致与歧义你的其他超载,因为两者现在匹配。您将需要按照OP中的建议限制第二次重载。

8

另一个答案已经解释了这个问题,但我认为有一个更好的解决方案。

如果要提取迭代器类型指向的类型,则应使用iterator_traits。在你的代码,更改第一个过载:

template<typename ForwardIt> 
auto f(ForwardIt first, ForwardIt) 
    -> enable_if<std::is_integral<typename std::iterator_traits<ForwardIt>::value_type>{}> 
{ 
    std::cout << "Integral container type" << std::endl; 
} 

,并使用相同与第二附加!。这是更具描述性的,因为代码对于它的功能非常清晰。

Live example

+0

+1仅用于* C++ 03编译的解决方案。好吧,无论如何,C++ 03 + TR1。 – Casey