2015-11-30 3 views
7

でアイテムを取得のstd ::タプルは、種類によって<code>std::tuple</code>項目を抽出するために、私は非常にきちんとして動作するコードを持っているC++ 11で継承されたタイプ

(私はこの機能を知っているようにさえplaced C++ 14 STLへ)今私は、コードに続いて継承されたクラス仕様

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 

で項目を選択するタスクに直面しています私の失敗し試みです​​:

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

'1'を持つためのユースケースは何ですか?あなたは、選択したいタプルに2つ以上の派生クラスを持つことを期待していますか? – AndyG

+0

@AndyG A/Bの横に別の値を持つタプルの例です。 (2)私は1つのインターフェイスの2降下を使用しないことを約束します – Dewfy

答えて

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

手動オーバーロードを追加する理由'&&'と 'std :: forward'を使用することはできませんか? – SirGuy

+0

@GuyGreer私はそれをきれいにしておきたいと思っていましたが、うまく、私はそれを転送参照のバージョンに置き換えました –

+0

申し訳ありませんが、私はあなたの手を強制するつもりはなかったし、それをきれいに保つことが私にとって良い理由のようです。 – SirGuy

3

まず、いくつかのメタプログラミングの定型文。

void_tはC++ 14である:

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

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

これは、リストの各要素に対してテストを実行し、通過する最初のテストを返す:

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

今はis_derived_fromを書き込み、そのベースから何かが派生しているかどうかをテストします。

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

abを作成する

私たちは Tから派生 tupleで最初のタイプを取得し、簡単な get_from_base<T>(tuple)、書くことができます
template<class Base, class List> 
using get_first_derived = 
    get_first_that_passes_t< 
    is_derived_from<Base>::template test, 
    List 
    >; 

、その後にstd::get<T>を呼び出す:2 OVE、我々はいくつかの塩基から誘導されたリストの最初の型を取得しますそれ:

template<class Base, class Tuple> 
auto get_from_base(Tuple&& tuple) 
->decltype(std::get< get_first_derived<Base, std::decay_t<Tuple>> >(std::forward<Tuple>(tuple))) 
    { return std::get< get_first_derived<Base, std::decay_t<Tuple>> >(std::forward<Tuple>(tuple)); } 

これをC++ 11に翻訳することは練習問題として残されています。 (おそらく_tを取り除くだけで十分です)。

Live example

書かれているように、Baseから派生した最初のタイプが見つかります。次に、というタイプのインスタンスがリストにない場合にのみ、その要素への参照が返されます。のインスタンスがない場合に限ります。

get<Type>と一致させるには、尾にBaseから派生した他のタイプがないことを確認したいと考えてください。

get_first_that_derives_fromが必要な場合は、タイプの代わりにインデックスを取得する必要があります。

+0

素晴らしい!ありがとう+1! – Dewfy

関連する問題