[英]Make struct behave like 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
返回所有成員。 這可行,但是需要在需要元組行為的所有地方調用此成員函數。
手動方式:
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); }
並在使用類似元組的方法之前包裝好電話。
首先, as_tuple
應該是類名稱空間中的自由函數。 這使您可以擴展其他人編寫的類型。
接下來,您應該嘗試在啟用ADL的上下文中調用get
。
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
。
一旦有了這個,我們就可以創建另一個helper基本類型:
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> )
重復此tag_t<E&> tuple_element( tag_t<T> )
。 然后,我們可以編寫一個tup::tuple_element<T, 0>
別名,您可以根據自己的tup::tuple_element<T, 0>
進行調度。
最后,修改與std::
tuple設施一起使用的現有代碼以使用tup tup::
設施。 它應與現有工作tuple
代碼,並且還將與來自繼承工種as_tuple_helper
具有friend as_tuple
定義。
但是,這不支持結構化綁定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.