2015-04-08 6 views
11

ich die folgende Template-Funktion geschrieben, die ein willkürlicher Behälter überprüft, ob ein bestimmtes Element enthält:wenn klassenspezifische Memberfunktion hat

template<template<class, class...> class container_t, class item_t, class... rest_t> 
bool contains(const container_t<item_t, rest_t...> &_container, const item_t &_item) { 
    for(const item_t &otherItem : _container) { 
     if(otherItem == _item) { return true; } 
    } 
    return false; 
} 

Das funktioniert gut für die meisten Container. Doch für alle Arten von Sätzen (und Karten) ist suboptimal, da könnten wir verwenden:

template<template<class, class...> class set_t, class item_t, class... rest_t> 
bool contains(const set_t<item_t, rest_t...> &_set, const item_t &_item) { 
    return _set.count(_item) > 0; 
} 

Natürlich sind wir nicht beide Vorlagen gleichzeitig wegen Mehrdeutigkeit verwenden können. Jetzt suche ich nach einer Möglichkeit, std::enable_if zu verwenden, um die erste Vorlage zu aktivieren, wenn container_t keine count Mitgliedsfunktion und die zweite Vorlage, wenn dies der Fall ist. Ich kann jedoch nicht herausfinden, wie Sie nach einer bestimmten Elementfunktion suchen (mit C++ 11).

+4

Wenn Sie bereit sind, Boost als Abhängigkeit hinzuzufügen, dann hat [boost.TTI] (http://www.boost.org/doc/libs/1_57_0/libs/tti/doc/html/index.html) was Sie suchen im '' BOOST_TTI_HAS_MEMBER_FUNCTION'' Makro –

+2

Sind Sie absolut sicher, dass Sie das tun möchten? In der Standardbibliothek können Sie genau festlegen, welche "Find" -Form Sie explizit verwenden möchten, wenn Sie eine lineare Suche durchführen und einen effizienteren Mechanismus verwenden. Als Randnotiz, wenn Sie dies wirklich tun wollen, verwenden Sie definitiv 'find' anstelle von' count', es vermeidet nur vorzeitige Pessimierung. –

+0

@SimonGibbons: Danke, es sieht so aus, wie ich es brauche. Allerdings würde ich es vorziehen, im Moment nicht von Boost abhängig zu sein. – Haatschii

Antwort

15

C++ 14-Funktion neu implementiert:

template<class...>struct voider{using type=void;}; 
template<class...Ts>using void_t=typename voider<Ts...>::type; 

A mini metaprogramming Bibliothek:

template<class...>struct types{using type=types;}; 
namespace details { 
    template<template<class...>class Z, class types, class=void> 
    struct can_apply : std::false_type {}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >: 
    std::true_type 
    {}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = details::can_apply<Z,types<Ts...>>; 

can_apply< some_template, args... > erbt von true_type iff some_template<args...> ein gültiger Ausdruck (in dem unmittelbaren Kontext).

Jetzt für Ihr Problem:

template<class T, class I> 
using dot_count_type = decltype(std::declval<T>().count(std::declval<I>())); 

template<class T, class I> 
using has_dot_count = can_apply<dot_count_type, T, I>; 

und has_dot_count ist eine Klasse, die Züge von true_type iff T.count(I) erbt ein gültiger Ausdruck ist.

namespace details { 
    template<class C, class I> 
    bool contains(std::false_type, C const& c, I const& i) { 
    for(auto&& x:c) { 
     if(x == i) { return true; } 
    } 
    return false; 
    } 
    template<class C, class I> 
    bool contains(std::true_type, C const& c, I const& i) { 
    return c.count(i) != 0; 
    } 
} 
template<class C, class I> 
bool contains(C const& c, I const& i) { 
    return details::contains(has_dot_count<C const&,I const&>{}, c, i); 
} 

, die Tag-Dispatching anstelle von SFINAE verwendet.

Verwenden find scheint wie eine bessere Idee als .count als beiseite. In der Tat sollten Sie in einem Fall .find den anderen find verwenden. In beiden Fällen sollten Sie using std::end; auto e = end(c); verwenden.

Nebenbei, MSVC 2013 (ich weiß nicht über 2015) nicht mit dieser Art von SFINAE oben verwendet. Sie nennen es "Ausdruck SFINAE". Sie verfügen über benutzerdefinierte Erweiterungen, um das Vorhandensein einer Elementfunktion zu erkennen. Aber das liegt daran, dass sie weit entfernt von C++ 11 Standard sind.

+1

VS 2015 (mindestens Vorschauversion) [unterstützt es auch nicht] (http://blogs.msdn.com/b/vcblog/archive/2014/11/17/c-11-14-17-features -in-vs-2015-preview.aspx) –

+0

Interessanter Ansatz. Funktioniert gut für mich (GCC 4.8). Vielen Dank. – Haatschii