2016-02-09 19 views
11

Đôi khi tôi thấy cần phải viết các thường trình chung có thể áp dụng cho vùng chứa đối tượng hoặc bản đồ các vùng chứa đó (ví dụ: xử lý từng vùng chứa trên bản đồ). Một cách tiếp cận là để viết thói quen riêng biệt cho các loại bản đồ, nhưng tôi nghĩ rằng nó có thể được tự nhiên hơn và ít tiết để có một thói quen mà làm việc cho cả hai loại đầu vào:Kiểm tra xem loại có phải là bản đồ

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

là gì, cách an toàn sạch sẽ để làm thẻ này gửi đi?

+2

Kiểm tra xem nó có lồng nhau loại 'key_type' và' mapped_type' không? –

Trả lời

12

Các kiểm tra câu trả lời hiện tại cho các tính chất rất cụ thể của std::map, hoặc là nó chính xác là một chuyên môn của std::map (điều này sẽ sai cho std::unordered_map hoặc các loại không chuẩn với cùng một giao diện như std::map) hoặc kiểm tra rằng value_type chính xác là std::pair<const key_type, mapped_type> (điều này đúng với multimapunordered_map, nhưng sai cho các loại không chuẩn với giao diện tương tự).

này chỉ kiểm tra mà nó cung cấp key_typemapped_type thành viên, và có thể được truy cập với operator[], vì vậy không nói rằng std::multimap là 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 có một "cơ sở đặc trưng" của một trong hai true_type hoặc false_type bạn có thể gửi về nó như vậy:

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

hoặc bạn có thể tránh cử hoàn toàn, và chỉ tình trạng quá tải foo cho bản đồ và không phải bản đồ:

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' khiến tôi cười khúc khích. :) – erip

+0

mặc dù, giải pháp này cũng chỉ hợp lệ khi bản đồ không chuẩn thực hiện giao diện của thùng chứa kết hợp tiêu chuẩn, điều này cũng có thể bị hỏng. –

+2

@DavidHaim, có chứ. Nếu loại không thực hiện một giao diện cụ thể thì có thể bạn không muốn gửi đến một hàm mẫu dựa trên giao diện đó. Đặc điểm này có thể dễ dàng được thiết kế để kiểm tra bất kỳ phần nào của giao diện bản đồ có liên quan đến 'foo' của OP hoạt động với bản đồ, cho bất kỳ định nghĩa nào về" bản đồ "có liên quan đến hàm. –

1

Đây là những gì tôi đã đưa ra:

#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

Xuất phát từ 'true_type' hoặc' false_type' đơn giản hơn việc xác định 'giá trị bool constexpr tĩnh'. Ngoài ra, đặc điểm của bạn cũng đúng đối với các multimaps, có thể không được mong muốn (chúng không có cùng giao diện). –

+0

@JonathanWakely Điểm tốt, đã cập nhật câu trả lời. Cảm ơn! – Daniel

10

này đã làm việc cho tôi, không được kiểm tra 100% mặc dù:

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

Đáng chú ý là điều này chỉ hoạt động cho 'std :: map' chứ không phải các loại 'bản đồ' khác (ví dụ:' std :: unordered_map'). Tôi nên có lẽ cụ thể hơn trong câu hỏi. – Daniel

+0

@Daniel sau đó thêm chuyên môn cho unordered_map nếu điều đó cần – Laurijssen

+0

@Daniel Tôi giả sử bạn đã nói về bản đồ thông thường chuẩn. điều này có thể dễ dàng được chuyển đổi thành hỗ trợ std :: unordered_map, và sau đó làAnyMap. mặc dù, tôi nghi ngờ rằng ai đó sẽ sử dụng nhiều hơn 1-2 loại bản đồ trong cùng một dự án. –

Các vấn đề liên quan