簡體   English   中英

使結構表現得像std :: tuple

[英]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_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::get或另一getx的命名空間基於關的重載決策規則。

我們可以創建類似的助手:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM