2015-11-30 15 views
7

in C++ 11 Ho codice molto ordinato e di lavoro per l'estrazione std::tuple voce per tipo (Per quanto ne so questa caratteristica anche placed a C++ 14 STL)std :: tuple, ottenere voce per tipo ereditato

ora sto affrontando con il compito di selezionare la voce dalla specifica classe ereditata

struct A 
{ 
    int a; 
}; 
struct B : public A 
{ 
    int b; 
}; 

... 
auto tval = std::make_tuple(1, B()); 
//now I would like to reference items as following: 
tuple_ref_by_inheritance<A>(tval).a = 5; //Access to B instance by parent A 

seguito il codice è il mio successo prova:

template< class T, class Tuple > 
    struct tuple_ref_index; 

    // recursive case 
    template<class T, class Head, class... Tail > 
    struct tuple_ref_index<T, std::tuple<Head, Tail...> > 
    { 
     enum { value = tuple_ref_index<T, std::tuple<Tail...>>::value + 1 }; 
    }; 

    template<class T, class Head, class... Tail > 
    struct tuple_ref_index<T, std::tuple<Head, Tail...> > 
    { 
     const static typename std::enable_if< 
       std::is_same<T, Head>::value>::type* _= nullptr; 
     enum { value = 0 }; 
    }; 

    template <class T, class Tuple> 
    inline T& tuple_ref_by_inheritance(Tuple& tuple) 
    { 
     return std::get< tuple_ref_index<T, Tuple>::value >(tuple); 
    } 
+0

Qual è il caso d'uso per avere il '1'? Si prevede di avere più di una classe derivata nella tupla da cui si desidera selezionare? – AndyG

+0

@AndyG solo un esempio di tupla che ha un altro valore accanto a A/B. (2) Prometto di non utilizzare 2 discendenti di un'interfaccia – Dewfy

risposta

5
#include <type_traits> 
#include <utility> 
#include <cstddef> 
#include <tuple> 

template <typename Base, typename Tuple, std::size_t I = 0> 
struct tuple_ref_index; 

template <typename Base, typename Head, typename... Tail, std::size_t I> 
struct tuple_ref_index<Base, std::tuple<Head, Tail...>, I> 
    : std::conditional<std::is_base_of<Base, Head>::value 
        , std::integral_constant<std::size_t, I> 
        , tuple_ref_index<Base, std::tuple<Tail...>, I+1> 
        >::type 
{ 
}; 

template <typename Base, typename Tuple> 
auto tuple_ref_by_inheritance(Tuple&& tuple) 
    -> decltype(std::get<tuple_ref_index<Base, typename std::decay<Tuple>::type>::value>(std::forward<Tuple>(tuple))) 
{ 
    return std::get<tuple_ref_index<Base, typename std::decay<Tuple>::type>::value>(std::forward<Tuple>(tuple)); 
} 

DEMO

+0

Perché aggiungere un sovraccarico manuale? Non è possibile usare '&&' e 'std :: forward'? – SirGuy

+0

@GuyGreer Volevo tenerlo pulito, ma va bene, l'ho sostituito con una versione di riferimento inoltro –

+0

Scusa, non intendevo forzare la tua mano, e tenerla pulita mi sembra una buona ragione. – SirGuy

3

Per prima cosa, alcune piastre di metaprogrammazione.

void_t è C++ 14:

namespace details { 
    template<class...>struct voider{using type=void;}; 
} 

template<class...Ts> 
using void_t=typename details::voider<Ts...>::type; 

Questo viene eseguito un test su ogni elemento di una lista, e restituisce il primo test che passa:

template<template<class...>class Test, class List> 
struct get_first_that_passes; 

template<template<class...>class Test, class List> 
using get_first_that_passes_t= 
    typename get_first_that_passes<Test,List>::type; 

namespace details { 
    template<template<class...>class, class, class...> 
    struct get_first_pass {}; 

    template<template<class...>class Test, class T0, class...Ts> 
    struct get_first_pass<Test, std::enable_if_t< !Test<T0>::value >, T0, Ts...> : 
    get_first_pass<Test, void, Ts...> 
    {}; 

    template<template<class...>class Test, class T0, class...Ts> 
    struct get_first_pass<Test, std::enable_if_t< Test<T0>::value >, T0, Ts...> { 
    using type=T0; 
    }; 
} 

template<template<class...>class Test, template<class...>class List, class...Ts> 
struct get_first_that_passes<Test, List<Ts...>>: 
    details::get_first_pass<Test, void, Ts...> 
{}; 

Ora scriviamo is_derived_from, che produce un test se qualcosa è derivato da una base:

template<class Base> 
struct is_derived_from { 
    template<class Derived> 
    using test = std::is_base_of<Base,Derived>; 
}; 

Componendo il ove due, si ottiene il primo tipo in un elenco che è derivata da alcune di base:

template<class Base, class List> 
using get_first_derived = 
    get_first_that_passes_t< 
    is_derived_from<Base>::template test, 
    List 
    >; 

che ci permette di scrivere un semplice get_from_base<T>(tuple), che ottiene il primo tipo in un tuple che deriva da T, quindi chiama std::get<T> su it:

la traslazione di questo in C++ 11 viene lasciato come esercizio. (rimuovere il numero _t è probabilmente sufficiente).

Live example.

Nota che come scritto, troverà il primo tipo derivato da Base. Quindi restituirà un riferimento a quell'elemento, ma solo se non ci sono più istanze di di tipo nell'elenco.

Per corrispondere a get<Type>, si desidera verificare che la coda non abbia altri tipi derivati ​​da Base.

Se si desidera get_first_that_derives_from, è necessario ottenere l'indice anziché il tipo.

+0

grandiosa! Grazie +1! – Dewfy

Problemi correlati