我写了一些管理元组列表的通用代码。现在我想使用该代码,但我想使用简单的结构,而不是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 :(得分:3)
手动方式:
UIView.noIntrinsicMetric
另一种方法是编写函数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
并在使用类似元组之前打包你的电话。
答案 1 :(得分:0)
首先,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)在启用ADL的上下文中调用tuple_size( tag_t<Foo> )
,或者(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
。
但是,这并不支持结构化绑定。