2016-02-09 4 views
11

Иногда мне приходится писать общие процедуры, которые могут быть применены к контейнеру объектов, или карта таких контейнеров (т. Е. Обрабатывать каждый контейнер на карте). Один из подходов заключается в написании отдельных процедур для типов карт, но я думаю, что это может быть более естественным и менее многословным иметь одну процедуру, которая работает для обоих типов ввода:Проверка наличия типа карты

template <typename T> 
auto foo(const T& items) 
{ 
    return foo(items, /* tag dispatch to map or non-map */); 
} 

Что такое безопасный, чистый способ сделать эта отправка тега?

+2

Проверьте, если он вложен '' key_type' и типы mapped_type'? –

ответ

12

Существующее ответил тест на очень специфических свойств std::map, что либо это точно специализация std::map (что было бы неверно для std::unordered_map или нестандартных типов с тем же интерфейсом, что и std::map), или тестирование того, что его value_type составляет точно std::pair<const key_type, mapped_type> (это верно для multimap и unordered_map, но для нестандартных типов с аналогичными интерфейсами).

This только тесты, которые она предоставляет key_type и mapped_type членов, и могут быть доступны с operator[], так не говорит, что std::multimap является mappish:

#include <type_traits> 

namespace detail { 
    // Needed for some older versions of GCC 
    template<typename...> 
    struct voider { using type = void; }; 

    // std::void_t will be part of C++17, but until then define it ourselves: 
    template<typename... T> 
    using void_t = typename voider<T...>::type; 

    template<typename T, typename U = void> 
    struct is_mappish_impl : std::false_type { }; 

    template<typename T> 
    struct is_mappish_impl<T, void_t<typename T::key_type, 
            typename T::mapped_type, 
            decltype(std::declval<T&>()[std::declval<const typename T::key_type&>()])>> 
    : std::true_type { }; 
} 

template<typename T> 
struct is_mappish : detail::is_mappish_impl<T>::type { }; 

Поскольку is_mappish имеет «базовые характеристики» либо true_type или false_type вы можете направить на него так:

template <typename T> 
auto foo(const T& items, true_type) 
{ 
    // here be maps 
} 

template <typename T> 
auto foo(const T& items, false_type) 
{ 
    // map-free zone 
} 

template <typename T> 
auto foo(const T& items) 
{ 
    return foo(items, is_mappish<T>{}); 
} 

или вы можете избежать диспетчерских полностью, и только от перегрузки foo для карт и не-карт:

template <typename T, 
      std::enable_if_t<is_mappish<T>{}, int> = 0> 
auto foo(const T& items) 
{ 
    // here be maps 
} 

template <typename T, 
      std::enable_if_t<!is_mappish<T>{}, int> = 0> 
auto foo(const T& items) 
{ 
    // map-free zone 
} 
+0

'is_mappish_impl' заставил меня усмехнуться. :) – erip

+0

Хотя это решение также действует только тогда, когда нестандартная карта реализует интерфейс стандартного ассоциативного контейнера, это также может нарушить. –

+2

@ Давид Хаим, да, очевидно. Если тип не реализует определенный интерфейс, вы, вероятно, не хотите отправлять функцию шаблона, которая полагается на этот интерфейс. Эта черта может быть легко адаптирована для проверки того, какие части интерфейса карты имеют отношение к OP 'foo', который работает с картами, поскольку любое определение« map »имеет отношение к функции. –

1

Вот что я придумал:

#include <type_traits> 
#include <utility> 

namespace detail 
{ 
    template <typename T, typename = void> 
    struct IsMap : std::false_type {}; 

    template <typename T> 
    struct IsMap<T, std::enable_if_t< 
         std::is_same<typename T::value_type, 
            std::pair<const typename T::key_type, 
               typename T::mapped_type> 
         >::value> 
    > : std::true_type {}; 
} 

template <typename T> 
constexpr bool is_map = detail::IsMap<T>::value; 

namespace { template <bool> struct MapTagImpl {}; } 
using MapTag = MapTagImpl<true>; 
using NonMapTag = MapTagImpl<false>; 

template <typename T> 
using MapTagType = MapTagImpl<is_map<T>>; 
+1

Выведение из 'true_type' или' false_type' проще, чем определение 'static constexpr bool value'. Также ваш признак верен для мультикадров, что может быть нежелательно (они не имеют одного и того же интерфейса). –

+0

@JonathanWakely Хорошие моменты, обновленный ответ. Благодаря! – Daniel

10

Это работает для меня, не тестируются на 100%, хотя:

template <class T> 
struct isMap { 
    static constexpr bool value = false; 
}; 

template<class Key,class Value> 
struct isMap<std::map<Key,Value>> { 
    static constexpr bool value = true; 
}; 

int main() { 
    constexpr bool b1 = isMap<int>::value; //false 
    constexpr bool b2 = isMap<std::vector<int>>::value; //false 
    constexpr bool b3 = isMap<std::map<int,std::string>>::value; //true 
    constexpr bool b4 = isMap<std::future<int>>::value; //false 
} 
+2

Стоит отметить, что это работает только для 'std :: map', а не для других типов 'map' (например,' std :: unordered_map'). Вероятно, я должен был быть более конкретным в вопросе. – Daniel

+0

@ Daniel затем добавьте специализацию для unordered_map, если это необходимо – Laurijssen

+0

@ Даниэль Я предполагал, что вы говорили о стандартной обычной карте. это можно легко преобразовать в поддержку std :: unordered_map, а затем isAnyMap. хотя я сомневаюсь, что кто-то будет использовать более 1-2 типов карт в одном проекте. –

Смежные вопросы