2015-04-08 9 views
11

ho scritto la seguente funzione del modello, che verifica se un contenitore arbitrario contiene un elemento specifico:Abilita funzione di modello se classe ha la funzione di membro specifica

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

Questo funziona bene per la maggior parte dei contenitori. Tuttavia, per tutti i tipi di insiemi (e mappe) è sub ottimale poiché ci si potrebbe usare:

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

Ovviamente non possiamo utilizzare entrambi i modelli contemporaneamente causa di ambiguità. Ora sto cercando un modo per utilizzare std::enable_if per consentire il primo modello a se container_t non fornisce una funzione count membro e il secondo modello se lo fa. Tuttavia non riesco a capire come controllare una funzione membro specif (usando C++ 11).

+4

Se siete disposti ad aggiungere spinta come una dipendenza, quindi [boost.TTI] (http://www.boost.org/doc/libs/1_57_0/libs/tti/doc/html/index.html) ha ciò stai cercando nella macro '' BOOST_TTI_HAS_MEMBER_FUNCTION'' –

+2

Sei assolutamente sicuro di volerlo fare? Nella libreria standard ti fanno capire esattamente quale forma di 'find' vuoi usare per renderla esplicitamente chiara quando stai facendo una ricerca lineare e quando usi un meccanismo più efficiente. Come nota a margine se vuoi davvero farlo, usa sicuramente 'find' invece di' count', evitando semplicemente una pessimizzazione prematura. –

+0

@SimonGibbons: Grazie, sembra quello di cui ho bisogno. Comunque preferirei non dipendere da boost al momento. – Haatschii

risposta

15

C++ 14 caratteristica, reimplementato:

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

una mini biblioteca metaprogrammazione:

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... > eredita da true_type IFF some_template<args...> è un'espressione valida (nel contesto immediato).

Ora per il vostro problema:

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>; 

e has_dot_count è una classe che eredita da tratti true_type se e solo se T.count(I) è un'espressione valida.

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

che utilizza l'invio di tag invece di SFINAE.

L'utilizzo di find sembra un'idea migliore di .count come una parte. Infatti, in un caso è necessario utilizzare .find l'altro find. In entrambi i casi è necessario utilizzare using std::end; auto e = end(c);.

Per inciso, MSVC 2013 (non so circa 2015) non gestisce questo tipo di SFINAE usato sopra. Lo chiamano "espressione SFINAE". Hanno estensioni personalizzate per rilevare l'esistenza di una funzione membro. Ma questo è dovuto al fatto che sono lontani dallo standard C++ 11.

+1

VS 2015 (almeno versione di anteprima) [non supporta altrettanto bene] (http://blogs.msdn.com/b/vcblog/archive/2014/11/17/c-11-14-17-features -in-vs-2015-preview.aspx) –

+0

Approccio interessante. Funziona bene per me (GCC 4.8). Grazie mille. – Haatschii

Problemi correlati