使struct的行为类似于std :: tuple

时间:2018-02-05 13:13:45

标签: c++ stdtuple

我写了一些管理元组列表的通用代码。现在我想使用该代码,但我想使用简单的结构,而不是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返回所有成员。这有效,但需要在我需要元组行为的所有地方调用此成员函数。

2 个答案:

答案 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_sizetuple_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::getget命名空间中的其他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

但是,这并不支持结构化绑定。