Заставьте struct вести себя как std :: tuple

Я написал некоторый общий код, который управляет списком кортежей. Теперь я хочу использовать этот код, но вместо std::tuple Я хотел бы использовать простые структуры, чтобы я мог получить доступ к переменным, используя имена вместо индикаторов. Есть ли простой способ заставить эти структуры вести себя как std::tuple, так что я могу использовать его с моим общим кодом?

struct foo {
int x;
float y;

// some code to enable tuple like behavior (e.g. std::get, std::tuple_size)
};

Я пытался добавить as_tuple функция-член, которая возвращает всех членов, используя std::tie, Это работает, но требует вызова этой функции-члена во всех местах, где мне нужно поведение кортежа.

0

Решение

Ручной способ:

struct foo {
int x;
float y;
};namespace std
{

template <>
class tuple_element<0, foo> {
using type = int;
};

template <>
class tuple_element<1, foo> {
using type = float;
};

template <std::size_t I>
tuple_element_t<I, foo>& get(foo&);

template <>
tuple_element_t<0, foo>& get(foo& f) { return f.x;}

template <>
tuple_element_t<1, foo>& get(foo& f) { return f.y; }

template <std::size_t I>
tuple_element_t<I, foo> get(const foo&);

template <>
tuple_element_t<0, foo> get(const foo& f) { return f.x;}

template <>
tuple_element_t<1, foo> get(const foo& f) { return f.y; }
}

Другой способ — написать функции as_tuple:

template <typename ... Ts>
std::tuple<Ts...>& as_tuple(std::tuple<Ts...>& tuple) { return tuple; }

std::tuple<int&, float&> as_tuple(foo& f) { return std::tie(f.x, f.y); }

и оберните свой вызов перед использованием кортежа.

3

Другие решения

Первый, as_tuple должна быть свободной функцией в пространстве имен класса. Это позволяет расширять типы, которые пишут другие люди.

Далее вы должны попытаться позвонить get в контексте с поддержкой ADL.

using std::get;
auto& x = get<1>(foo);

если ты сделаешь это, мы сможем осуществить магию.

struct get_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend decltype(auto) get( T&& t ) {
return std::get<I>( as_tuple( std::forward<T>(t) ) );
}
};

сейчас

struct foo:get_from_as_tuple {
int x;
float y;

friend auto as_tuple( get_from_as_tuple const& self ) {
return std::tie( self.x, self.y );
}
};

мы можем это сделать:

foo f;
using std::get;
std::cout << get<0>(f) << "," << get<1>(f) << "\n";

Теперь это по-прежнему не позволяет tuple_size а также tuple_element,

Нет тривиального способа выполнить эту часть, но мы можем обойти это.

#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }

namespace tup {
namespace adl_get {
using std::get;
template<std::size_t I,
class T
>
auto get_helper( T&& t )
RETURNS( get<I>(std::forward<T>(t) ) )
}
template<std::size_t I, class T>
auto get( T&& t )
RETURNS(adl_get::get_helper<I>(std::forward<T>(t)))
}

сейчас tup::get<7>( x ) отправит либо std::get или другой get в xпространство имен, основанное на правилах разрешения перегрузки.

Мы можем создать похожих помощников:

namespace util {
template<class T>
struct tag_t {constexpr tag_t(){}};
template<class T>
constexpr tag_t<T> tag{};
}
namespace tup {
namespace adl_tuple_size {
template<class T>
constexpr std::size_t get_tuple_size( tag_t<T>, ... ) {
return std::tuple_size<T>::value;
}
template<class T>
constexpr auto get_tuple_size( tag_t<T>, int )
RETURNS( tuple_size( tag_t<T> ) )
}
template<class T>
constexpr std::size_t tuple_size() {
return adl_tuple_size::get_tuple_size( tag<T> );
}
}

сейчас tup::tuple_size<Foo>() это constexpr звонок, который получает размер Foo либо (A) вызывая tuple_size( tag_t<Foo> ) в контексте с поддержкой ADL или (B) возвращая std::tuple_size<Foo>::value,

Получив это, мы можем создать еще один базовый тип помощника:

struct tuple_size_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend std::size_t tuple_size( T&& t ) {
return std::tuple_size< decltype(as_tuple( std::forward<T>(t) ) ) >::value;
}
};

struct as_tuple_helpers : get_from_as_tuple, tuple_size_from_as_tuple {};

struct foo:as_tuple_helpers {
// ..
};

и теперь у нас есть 2 примитива.

Повторите это для tag_t<E&> tuple_element( tag_t<T> ), Тогда мы можем написать tup::tuple_element<T, 0> псевдоним, который отправляет, как вам нравится.

Наконец, адаптируйте существующий код, который работает с std:: кортежи для использования tup:: объекты. Должно работать с существующими tuple код, а также будет работать с типами, унаследованными от as_tuple_helper который имеет friend as_tuple определены.

Это, однако, не дает вам поддержки структурированных привязок.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector