[英]std::tuple, get item by inherited type
在c ++ 11中,我有一個非常整潔和工作的代碼,用於按類型提取std::tuple
項目(因為我知道這個功能甚至放在 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);
}
#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));
}
首先,一些元編程樣板。
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>;
};
通過編寫上面的兩個,我們得到一個從一些基礎派生的列表中的第一個類型:
template<class Base, class List>
using get_first_derived =
get_first_that_passes_t<
is_derived_from<Base>::template test,
List
>;
這讓我們編寫一個簡單的get_from_base<T>(tuple)
,它獲取從T
派生的tuple
中的第一個類型,然后在其上調用std::get<T>
:
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
s可能就夠了)。
實例 。
請注意,如上所述,它將找到從Base
派生的第一個類型。 然后它將返回對該元素的引用,但前提是列表中不再有該類型的實例。
要匹配get<Type>
,您需要確認尾部沒有從Base
派生的其他類型。
如果你想要get_first_that_derives_from
,你必須得到索引而不是類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.