2014-02-09 1 views
13
template<class> struct Printer; 

// I want this to match std::vector (and similar linear containers) 
template<template<class, class...> class T, class TV, class... TS> 
    struct Printer<T<TV, TS...>> { ... }; 

// I want this to match std::map (and similar map-like containers) 
template<template<class, class, class...> class TM, class TK, class TV, typename... TS> 
    struct Printer<TM<TK, TV, TS...>> { ... } 

int main() 
{ 
    // Both of these match the second specialization, which is only intended 
    // for std::map (and similar map-like containers) 
    Printer<std::vector<int>>::something(); 
    Printer<std::map<int, float>>::something(); 
} 

std::vectorstd::map 두 번째 전문화 일치합니다. 나는 std::vector의 할당 자 매개 변수가 과 일치하게되었는데, 이는 std::map의 값을위한 것입니다. 제 전문화와명확 템플릿 특수화는

I 일치 어떻게 std::vector(컨테이너 선 등) 및 제 하나 std::map(다른 키값 용기)? 여기

+1

'template' 인수의 개수는 문제를 해결하기위한 끔찍한 방법입니다. 즉, 깨지기 쉽고 구현에 대한 세부 사항에 달려 있습니다. – Yakk

+0

올바른 (또는 적어도 표준화 된) 용어는 ** 순차적 컨테이너 ** 및 ** 연관 컨테이너 **입니다. * nb,이 의견은 Google보다 더 많은 것입니다. * – Orwellophile

답변

15

문제는 모든 단일 컨테이너에 대한 당신이 전문화를 작성하는 경우는 오직 일 것입니다. 지루한 작업입니다.

대신 다른 속성에 의존 할 수

  • 용기는 반드시
  • 이 위에
  • 는, 연관 컨테이너가 다른 사람의 사이에서하는 ::key_type 중첩 된 유형이있을 것이다 begin(c)end(c) 표현을 통해 반복 가능한을 넘는 것 , § 23.2.4 [associative.rqmts].

따라서, 우리는 태그 파견을 기반으로하는 분류를 채찍질 할 수 있습니다

inline constexpr auto is_container_impl(...) -> std::false_type { 
    return std::false_type{}; 
} 

template <typename C> 
constexpr auto is_container_impl(C const* c) -> 
    decltype(begin(*c), end(*c), std::true_type{}) 
{ 
    return std::true_type{}; 
} 

template <typename C> 
constexpr auto is_container(C const& c) -> decltype(is_container_impl(&c)) { 
    return is_container_impl(&c); 
} 

inline constexpr auto is_associative_container_impl(...) 
    -> std::false_type 
{ return std::false_type{}; } 

template <typename C, typename = typename C::key_type> 
constexpr auto is_associative_container_impl(C const*) -> std::true_type { 
    return std::true_type{}; 
} 

template <typename C> 
constexpr auto is_associative_container(C const& c) 
    -> decltype(is_associative_container_impl(&c)) 
{ 
    return is_associative_container_impl(&c); 
} 

을 그리고 지금 당신이 쓸 수있는 "간단한"코드 :

template <typename C> 
void print_container(C const& c, std::false_type/*is_associative*/) { 
} 

template <typename C> 
void print_container(C const& c, std::true_type/*is_associative*/) { 
} 

template <typename C> 
void print_container(C const& c) { 
    return print_container(C, is_assocative_container(c)); 
} 

이제,이 수도 이 요구 사항에서 set은 연관 컨테이너이지만 그 값은 pair이 아니므로을 인쇄 할 수 없기 때문에 원하는 것은 정확하지 않을 수 있습니다.. 당신은 필요에 따라 태그를 파견해야합니다.

+1

기술적으로 당신은'iterator_traits '와'iterator_traits '가 존재하는지 검사해야합니다. 'std :: begin 사용하기 '를 포함하는 ADL 문맥에서 동일하다. use std :: end;'는 반복 가능한 범위인지 판단합니다. 아직도 컨테이너인지는 모르겠지만 상관하지 않습니다. :) – Yakk

+0

우아한 코드, C++ 11의 멋진 사용! 처음에는 이것이 (std :: begin (nArray')가 유효하기 때문에) 컨테이너로 C 배열을 매치하는 것처럼 보이지만 포인터를'C const * c'와 함께 사용하면 더 이상 케이스. 완벽! –

+0

@NikosAthanasiou : 실제로 C 배열을 인쇄하려고했는데 (실제로 인쇄 할 수는 있었지만), 솔직하게 말해서 나는 그것을 테스트해야 할 것입니다. 부패하지 않겠지 만 나는 그렇지 않을 것이라고 생각했다). –

1

문제는 그

template <class, class...> T 

template <class, class, class...> TM 

두 경기 모두 당신의 예제의 경우 적어도 2 개 템플릿 매개 변수가있는 템플릿 클래스를. http://coliru.stacked-crooked.com/a/7f6b8546b1ab5ba9

또 다른 가능성 :

template <class> 
struct Printer; 

template <template<typename, typename> class C, template <typename> class A, typename T> 
struct Printer< C<T, A<T>> > { 
    ... 
}; 

template <template<typename, typename, typename, typename> class C, template <typename> class Comp, template <typename> class A, typename K, typename T> 
struct Printer< C<K, T, Comp<K>, A<std::pair<const K,T>>> > { 
    ... 
}; 

당신은이 표준 : : 벡터 및 표준에 대한 작업 ::지도 여기에서 볼 수 있습니다 : 당신이 할 수있는 한 가지 예를 들어 같은 두 템플릿 매개 변수 목록을보다 구체적으로 확인하는 것입니다 SFINAE을 사용하는 것입니다 (실제로 나는 두 시나리오에서 사용 권하고 싶습니다) :

template<template<class, class...> class T, class TV, class... TS, class = typename std::enable_if<std::is_same<T, std::vector>::value>::type> 
struct Printer<T<TV, TS...>> { ... }; 

template<template<class, class, class...> class TM, class TK, class TV, typename... TS, class = typename std::enable_if<std::is_same<T, std::map>::value>::type> 
struct Printer<TM<TK, TV, TS...>> { ... } 

편집 : Oups, 단지 의견 읽기 당신이 뭔가를 일치 싶어 '표준 : vector' 같은 특정 표준 없습니다, ::벡터. 그러나 첫 번째 방법은 적어도 std :: vector와 std :: map을 구분해야합니다. 다른 방법으로 컨테이너를 반복 알고리즘으로 작성하려는 경우 반복기에 대한 함수를 작성하고 이들을 구별하는 것이 좋습니다.

편집 2 : 이전의 코드는 비참하게 잘못되었습니다. 그러나 그것은 지금 작동합니다.

+1

첫 번째 방법은 일반적인 컨테이너 특성을 작성하는 데 사용할 수 있습니다 (http://functionalcpp.wordpress.com/2013/08/22/container-traits)./나는 이것이 당신이 찾고있는 것이라고 인상을받습니다 ... –

3

순차적 또는 '키 - 값'이 아닌 컨테이너가 있으므로 질문이 다소 모호합니다. set. 연관 컨테이너와 시퀀스를 구별하는 의미가 있습니까?

그런 경우 연관 컨테이너가 key_type 인 반면 시퀀스 컨테이너는 그렇지 않다는 사실에 의존 할 수 있습니다. 여기에 솔루션입니다 : 패턴 매칭 방식으로

#include <type_traits> 
#include <vector> 
#include <map> 

template<class, class = void> 
struct IsAssociativeContainer 
    : std::false_type {}; 

template<class T> 
struct IsAssociativeContainer<T, 
    typename std::enable_if<sizeof(typename T::key_type)!=0>::type> 
    : std::true_type {}; 

template<class T, bool = IsAssociativeContainer<T>::value> 
struct Printer; 

// I want this to match std::vector (and similar linear containers) 
template<template<class, class...> class T, class TV, class... TS> 
    struct Printer<T<TV, TS...>, false> { static void something(); }; 

// I want this to match std::map (and similar map-like containers) 
template<template<class, class, class...> class TM, class TK, class TV, typename... TS> 
    struct Printer<TM<TK, TV, TS...>, true> { static void something(); }; 

int main() 
{ 
    // Both of these match the second specialization, which is only intended 
    // for std::map (and similar map-like containers) 
    Printer<std::vector<int>>::something(); 
    Printer<std::map<int, float>>::something(); 
} 

Live example