Jak wykryć kontenery asocjacyjne w czasie kompilacji?

1

Mam prostą funkcję

template <typename Container, typename Value>
bool contains(const Container& container, const Value& value)
{
    return std::find(std::begin(container), std::end(container), value) != std::end(container);
}

Dla kontenerów asocjacyjnych (set, multiset, unordered_set, map itd.) można wykorzystać metodę find(), która jest szybsza niż globalne find(). Pytanie czy da się w prosty i elegancki sposób wykryć taką sytuację, żeby wywołać container.find(), bo nie uśmiecha mi się pisać specjalizacji dla każdego typu kontenera.

0

Mają być konkretnie te cztery, czy dowolny kontener zawierający callable find z parametrem konwertowalnym z value?

I w sumie który C++ targetujesz? 14?

0

W tej chwili potrzebuję tylko dla 6 kontenerów (set, multiset, unordered_set, map, multimap, unordered_map), więc rozwiązanie nie musi być super generyczne.
C++11.

3
namespace detail
{
template<int Level> struct select_overload;
template<> struct select_overload<0>{};
template<int Level> struct select_overload : select_overload<Level-1>{};

template<typename C, typename V>
auto contains(C&& c, V&& v, select_overload<1>) -> decltype(c.find(forward<V>(v)) == c.end()) {
    BARK;
    return c.find(forward<V>(v)) != c.end();
}

template <typename Container, typename Value>
bool contains(const Container& container, const Value& value, select_overload<0>)
{
    BARK;
    return std::find(std::begin(container), std::end(container), value) != std::end(container);
}

}

template<typename C, typename V>
bool contains(C&& c, V&& v)
{
    return detail::contains(forward<C>(c), forward<V>(v), detail::select_overload<1>{});
}

https://wandbox.org/permlink/0DF1muaWEGZkEv0l

To powinno działać i być zgodne z C++11.




Oryginalny post

Tak na szybko:

namespace detail
{
template<typename C, typename V>
auto contains(C&& c, V&& v) -> decltype(c.find(forward<V>(v)) == c.end()) {
    BARK;
    return c.find(forward<V>(v)) != c.end();
}

template <typename Container, typename Value>
bool contains(const Container& container, const Value& value)
{
    BARK;
    return std::find(std::begin(container), std::end(container), value) != std::end(container);
}

}

template<typename C, typename V>
bool contains(C&& c, V&& v)
{
    return detail::contains(forward<C>(c), forward<V>(v));
}

https://wandbox.org/permlink/RjkyEmaaKihZMu6L

Przy czym to nie jest rozwiązanie idealne, bo czasem zły szablon może być uznany za lepszy:

    const set<int> s{1,2,3};
    const int i = 1;
    contains(s, i); // wybierze złą funkcję
3

Bazując mocno na TYM skompilawałem coś takiego ideone.
Output

'find' in 'B' : true
'find' in 'std::map' : true
'find' in 'std::vector' : false

Tylko, że to zawiera makra (ble) także tak sobie.

1

Jeszcze ku potomności ;​) wersja słowo w słowo zgodna z wymaganiami autora wątku (czyli wykrywanie konkretnych typów):

namespace detail
{
template<typename T> struct is_special : false_type {};
template<typename... Ps> struct is_special<set<Ps...>> : true_type {};
template<typename... Ps> struct is_special<map<Ps...>> : true_type {};

template<typename C, typename V>
bool contains(C&& c, V&& v, true_type) {
    BARK;
    return c.find(forward<V>(v)) != c.end();
}

template <typename Container, typename Value>
bool contains(const Container& container, const Value& value, false_type)
{
    BARK;
    return std::find(std::begin(container), std::end(container), value) != std::end(container);
}

}

template<typename C, typename V>
bool contains(C&& c, V&& v)
{
    using decayed = typename decay<C>::type;
    using is_special = detail::is_special<decayed>;
    return detail::contains(forward<C>(c), forward<V>(v), is_special{});
}

https://wandbox.org/permlink/9HV9cTjjkudzS5oe

1 użytkowników online, w tym zalogowanych: 0, gości: 1