В следующей программе обе функции вызывают печать «Неинтегральная перегрузка», хотя у меня есть 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<...>>
на второй перегрузке но безрезультатно.
Для типа итератора foo
, decltype(*foo)
будет foo::value_type&
, Тип ссылки определенно не является интегральным. Вам нужно удалить ссылку (и, возможно, также cv-qualification, IIRC) перед оценкой типа с помощью std::is_integral
черта, которая легко делается с std::decay
черта типа преобразования:
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.
Другой ответ уже объяснил проблему, но я думаю, что есть лучшее решение.
Если вы хотите извлечь тип, на который указывает тип итератора, вы должны использовать 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;
}
и использовать то же самое с дополнительным !
На втором. Это более наглядно, так как код достаточно ясно показывает, что он делает.