Jak wykryć kontenery asocjacyjne w czasie kompilacji?

Odpowiedz Nowy wątek
2017-05-24 15:00

Rejestracja: 5 lat temu

Ostatnio: 4 miesiące temu

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.

edytowany 1x, ostatnio: twonek, 2017-05-24 15:05

Pozostało 580 znaków

kq
2017-05-24 15:02
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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?


edytowany 1x, ostatnio: kq, 2017-05-24 15:03

Pozostało 580 znaków

2017-05-24 15:04

Rejestracja: 5 lat temu

Ostatnio: 4 miesiące temu

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.

edytowany 1x, ostatnio: twonek, 2017-05-24 15:05

Pozostało 580 znaków

kq
2017-05-24 15:13
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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ę

edytowany 5x, ostatnio: kq, 2017-05-24 15:45
SFINAE po typie zwracanym, na to bym nie wpadł - twonek 2017-05-24 15:33
Aye, expression SFINAE ❤. Nie wiem jak z wsparciem w MSVC, jak ostatnio się tym bawiłem (dawno) to nie działało, ale to było w czasach VS ~2013. - kq 2017-05-24 15:34

Pozostało 580 znaków

2017-05-24 15:25

Rejestracja: 10 lat temu

Ostatnio: 52 sekundy temu

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.


edytowany 1x, ostatnio: several, 2017-05-24 15:32
That's so C++03 :​P - kq 2017-05-24 15:25
Sam z głowy nie znałem odpowiedzi, to zapostowałem chociażby żeby przetrwał pierwszy link ;) - several 2017-05-24 15:28
Sam na ten temat posta kiedyś popełniłem (tutaj), ale trzeba powiedzieć, że expression sfinae jest tutaj duuużo wygodniejszym rozwiązaniem. - kq 2017-05-24 15:29
Ta technika ze zwracanymi typami różnej wielkości jest dosyć popularna, w C++ Template Metaprogramming jest opisana jako jedna z tych podstawowych rzeczy do opanowania. - twonek 2017-05-24 15:37
@kq: Najpewniej. Byłby czas, żebym w końcu SFINAE zrozumiał. - several 2017-05-24 15:37
Jest popularna - bo była jedynym rozwiązaniem problemu przed C++11. W nowym kodzie to hm.. O ile nie powiedziałbym anty-idiom, to na pewno przestarzałe rozwiązanie (teraz mamy variadic templates, void_t, expression sfinae, decltype). - kq 2017-05-24 15:39

Pozostało 580 znaków

kq
2017-05-24 15:40
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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


edytowany 1x, ostatnio: kq, 2017-05-24 15:41
wersja poprawniejsza, ale nie tak ładna jak ta z SFINAE, bo wymaga dodania is_special dla wszystkich typów asocjacyjnych :P - twonek 2017-05-24 15:52
Tak jest, ale być może kiedyś ktoś tu trafi z google'a, kto będzie chciał ręcznie wybrać, które typy jak mają się zachowywać, to dałem tę możliwość :​) - kq 2017-05-24 15:54

Pozostało 580 znaków

Odpowiedz

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