2014-02-09 47 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的分配器參數匹配到TV,這是爲std::map的值。歧義模板特

我怎樣才能匹配std::vector(和其他線性容器)與第一專業化和std::map(和其他鍵 - 值容器)與第二個

+1

請注意,模板參數的數量是一個可怕的方式來解決您的問題:它是脆弱的,取決於你不應該關心的實現細節。 – Yakk

+0

正確的(或至少是標準化的)術語是** Sequential Container **和** Associative Container **。 * nb,這個評論更多的是Google,比你更多。* – Orwellophile

回答

15

與模式匹配方法的問題是,如果爲每一個容器,你寫一個專業化它僅會工作。這是一項繁瑣的工作。

相反,你可以依靠其他屬性:

  • 容器必然是迭代在通過begin(c)end(c)表達
  • 在此之上,關聯容器將有一個::key_type嵌套類型,等等如§23.2.4所述[聯想。rqmts]

因此,我們可以掀起一個分類的基礎上,tag分派

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,所以你不能打印key: value。您必須根據您的需求調整標籤分配。

+1

從技術上講,你應該檢查是否存在'iterator_traits '和'iterator_traits 在ADL上下文中是相同的,包括'using std :: begin;使用std :: end;'來確定是否有可迭代的範圍。仍然不知道它是否是一個容器,但可能不在乎。 :) – Yakk

+0

優雅的代碼,真棒使用C++ 11!起初,它看起來好像這將匹配C數組作爲容器(因爲'std :: begin(nArray')是有效的),但是你利用衰減到指針(使用'C const * c'),這不再是案件。無瑕! –

+0

@NikosAthanasiou:實際上它的目的是匹配C數組(儘管它們可以被打印),說實話,我將不得不測試它是否存在(不記得我的頭頂是否會出現'&c'衰退與否,但我認爲它不會)。 –

1

的這裏的問題是,

template <class, class...> T 

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

都匹配的是至少有2個模板參數是在這兩個你的例子而言,任何模板類。有一兩件事你可以做的是使這兩個模板參數列表更具體的,例如像:

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>>> > { 
    ... 
}; 

你可以看到它工作的std :: vector和std ::地圖位置:http://coliru.stacked-crooked.com/a/7f6b8546b1ab5ba9

另一種可能是使用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,只是在評論讀你想匹配的東西「的std :: vector'狀,沒有特別STD ::向量。然而,第一種方法至少應該區分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