2016-08-01 1 views
3

반복자를 사용하는 C++ 11에서 템플릿 함수를 구현하고 싶습니다. 값 유형이 임의 유형의 std::pair 인 반복기 쌍이 전달되면 구현시 일부 특수 처리를 수행해야합니다. 나는 다음과 같은 정의를 마련하려고 : C++에서 다른 반복자 value_types에 대한 함수를 오버로드하는 방법

std::vector<int> int_vec{{1,2,3,4}}; 
process(int_vec.begin(), int_vec.end()); 

가 제대로 기능 process의 첫 번째 정의를 호출

// arbitrary value types 
template<typename Iter> 
void process(Iter begin, Iter end) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << *iter << "\n"; 
    } 
} 

// std::pair value types 
template<typename Iter, typename First, typename Second, 
    typename std::enable_if< 
     std::is_same< 
      typename std::iterator_traits<Iter>::value_type, std::pair<First,Second> 
      >::value 
     >::type* = 0> 
void process(Iter begin, Iter end) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << (*iter).first << " " << (*iter).second << "\n"; 
    } 
} 

다음 예제 코드를 사용. 그러나

std::vector<std::pair<int,std::string>> pair_vec{ 
    {std::make_pair(1, "First"), std::make_pair(2, "Second")}}; 
process(pair_vec.begin(), pair_vec.end()); 

도 (연타를 사용) 오류 메시지의 첫 번째 정의 및 결과를 호출

error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::__1::pair<int, std::__1::basic_string<char> >') 

왜 컴파일러는이 경우에 두 번째 정의를 선택하지 않는 이유는 무엇입니까? 오버로드 된 함수를 어떻게 변경해야합니까?

답변

1

템플릿 인수 인 FirstSecond은 템플릿 서명 공제에 대한 유일한 인수 인 Iter에서 추론 할 수 없습니다.이 인수는 함수 서명에 필요한 시점에서 나타납니다.

당신은, 그러나, (아래 예 IsPair에) 헬퍼 클래스를 사용하여 FirstSecond을 추론하고, 두 가지 옵션 중 선택이 템플릿 특수화를 사용하는 Iter를 사용할 수 있습니다.

예 :

template<typename T> 
struct IsPair 
{ 
    static const bool value = false; 
}; 

template<typename First, typename Second> 
struct IsPair<std::pair<First, Second>> 
{ 
    static const bool value = true; 
}; 

// arbitrary value types 
template<typename Iter> 
void process(Iter begin, Iter end, 
      typename std::enable_if< 
       !IsPair<typename std::iterator_traits<Iter>::value_type>::value 
      >::type* = 0) 
{ 
    for (Iter iter = begin; iter != end; ++iter) 
    { 
     std::cout << *iter << "\n"; 
    } 
} 

// std::pair value types 
template<typename Iter> 
void process(Iter begin, Iter end, 
      typename std::enable_if< 
       IsPair<typename std::iterator_traits<Iter>::value_type>::value 
      >::type* = 0) 
{ 
    for (Iter iter = begin; iter != end; ++iter) 
    { 
     std::cout << (*iter).first << " " << (*iter).second << "\n"; 
    } 
} 

Working example on coliru

1

왜 예를 파견 태그를 사용하지 : enable_if를 사용하도록 시도에서

#include <iostream> 
#include <vector> 
#include <utility> 

template <class> struct tag { }; 

template <class Iter, class T> 
void process_impl(Iter begin, Iter end, tag<T>) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << *iter << "\n"; 
    } 
} 

template <class Iter, class First, class Second> 
void process_impl(Iter begin, Iter end, tag<std::pair<First, Second>>) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << (*iter).first << " " << (*iter).second << "\n"; 
    } 
} 

template <class Iter> 
void process(Iter begin, Iter end) { 
    process_impl(begin, end, tag<typename Iter::value_type>{}); 
} 

int main() { 
    std::vector<int> int_vec{{1,2,3,4}}; 
    process(int_vec.begin(), int_vec.end()); 
    std::vector<std::pair<int,std::string>> pair_vec{{std::make_pair(1, "First"), std::make_pair(2, "Second")}}; 
    process(pair_vec.begin(), pair_vec.end()); 
} 
3

, 당신은 두 개의 템플릿 매개 변수를 도입하고 그 추론되지 않은 문맥입니다. 컴파일러가 과 Second이 무엇인지 결정할 방법이 없으므로 오버로드가 항상 오버로드 집합에서 제거됩니다. 그것은 결코 고려되지 않습니다.

나는 SFINAE를 전적으로 피하는 것이 좋습니다. 당신이 간단하고있는 것은 모든 요소의 범위를 일부 실제 수행과 관련된 경우, 단지 함수 오버로드를 사용

template <class T> 
void process_impl(T const& elem) { 
    // generic case 
    std::cout << elem << '\n'; 
} 

template <class T, class U> 
void process_impl(std::pair<T, U> const& elem) { 
    // overload for pair 
    std::cout << elem.first << ' ' << elem.second << '\n'; 
} 

template <class Iter> 
void process(Iter first, Iter last) { 
    // if C++14 
    std::for_each(first, last, [](auto&& elem){ process_impl(elem); }); 

    // if C++11 
    using E = typename std::iterator_traits<Iter>::reference; 
    std::for_each(first, last, [](E elem) { process_impl(elem); }); 
} 
1

두 번째 전문화가 호출되지 않습니다 컴파일러는 FirstSecond 템플릿 형식 인수를 추론 할 수 적이 없기 때문에 . 대신에 주어진 유형이 std::pair의 전문 분야인지 확인하는 특성이 필요합니다. 여기에 당신이 할 수있는 것입니다 : 같은 특성을 가진 지금

template <template <typename ...> class Ref, typename T> 
struct is_template_specialization : std::false_type {}; 

template <template <typename ...> class Ref, typename ... Args> 
struct is_template_specialization<Ref, Ref<Args...>> : std::true_type {}; 

template <typename T> 
using is_pair = is_template_specialization<std::pair, T>; 

, 당신의 SFINAE 조건이된다 :

// std::pair value types 
template<typename Iter, 
    typename std::enable_if< 
     is_pair<typename std::iterator_traits<Iter>::value_type 
      >::value 
     >::type* = 0> 
void process(Iter begin, Iter end) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << (*iter).first << " " << (*iter).second << "\n"; 
    } 
} 

문제가 발생 : 당신이 사용하는 한 쌍의 참됩니다. 사용 가능한 두 가지 오버로드가 있습니다 ...이 호출은 모호하게, 그래서 당신은 일반적인 구현에 SFINAE 조건을 추가해야합니다

// arbitrary value types 
template<typename Iter, 
    typename std::enable_if< 
     ! is_pair<typename std::iterator_traits<Iter>::value_type>::value 
    >::type* = nullptr> 
void process(Iter begin, Iter end) { 
    for (Iter iter = begin; iter != end; ++iter) { 
     std::cout << *iter << "\n"; 
    } 
} 

이이 live demo

에서 입증 된 바와 같이 하나의 구현은 주어진 통화를 사용할 수 있는지 확인합니다
관련 문제