元组视图的元素类型

时间:2016-07-14 19:52:41

标签: c++ templates c++11 c++14

我正在设计一个实用程序来创建一个类似于元组的对象中的视图。目标是启用这样的代码:

// make_tuple_view<Is...>(t): view of t's elements at positions Is...

auto t = std::make_tuple(42, 'c', 3.14);
auto tv = make_tuple_view<2, 1, 0, 1>(t);  // tv == (3.14, 'c', 42, 3.14)
++std::get<0>(tv);
// tv == (4.14, 'c', 42, 4.14)
// t  == (42, 'c', 4.14)

因此元组视图的元素类型是对原始元组中相应元素的适当类型的引用。目前,这是我推断出引用类型的方式:

#include <tuple>
#include <utility>

namespace detail {

template<std::size_t FromIndex, std::size_t... Is, typename Tuple>
constexpr auto make_tuple_range_impl(std::index_sequence<Is...>,
                                     Tuple&& t) noexcept
{
    return std::forward_as_tuple(
            std::get<FromIndex + Is>(std::forward<Tuple>(t))...);
}

}  // namespace detail

// make_tuple_range
template<std::size_t FromIndex, std::size_t ToIndex, typename Tuple>
constexpr auto make_tuple_range(Tuple&& t) noexcept
{
    static_assert(FromIndex <= ToIndex,
                  "FromIndex must be less than or equal to ToIndex");

    return detail::make_tuple_range_impl<FromIndex>(
            std::make_index_sequence<ToIndex - FromIndex>(),
            std::forward<Tuple>(t));
}

// make_tuple_view
template<std::size_t... Is, typename Tuple>
constexpr auto make_tuple_view(Tuple&& t) noexcept
{
    return std::forward_as_tuple(std::get<Is>(std::forward<Tuple>(t))...);
}

Tests

double pi = 3.14;
std::tuple<int, double&, const char, float> t(42, pi, 'c', 0);

// non-const lvalue
static_assert(std::is_same<
                  decltype(make_tuple_range<0, 3>(t)),
                  std::tuple<int&, double&, const char&>
              >::value, "");

// const lvalue
const auto& ct = t;
static_assert(std::is_same<
                  decltype(make_tuple_view<3, 0, 2, 1>(ct)),
                  std::tuple<const float&, const int&, const char&, double&>
              >::value, "");

// non-const rvalue
static_assert(std::is_same<
                  decltype(make_tuple_range<1, 4>(std::move(t))),
                  std::tuple<double&, const char&&, float&&>
              >::value, "");

// const rvalue
const auto&& crt = std::move(t);
static_assert(std::is_same<
                  decltype(make_tuple_range<1, 4>(std::move(crt))),
                  std::tuple<double&, const char&, const float&>
              >::value, "");

但是,我对我的实施并不完全有信心。是否存在可以推导出错误引用类型的边缘情况?

更新:感谢所有精彩的答案,TIL肯定!但是,我仍然想知道我对make_tuple_view/range的定义是否可以推断出错误的返回类型。这个问题的目的不是寻找最佳实现,而是要了解类型系统的这一部分的细节。如果可以对我的make_tuple_view/range函数的推导返回类型进行更多关注,我真的很感激。

2 个答案:

答案 0 :(得分:6)

namespace details {
  template<std::size_t...Is,class TupleIn>
  auto tuple_view( std::index_sequence<Is...>, TupleIn&& tin) {
    return std::forward_as_tuple( std::get<Is>(std::forward<TupleIn>(tin))... );
  }
}
template<class TupleIn>
auto tuple_view(TupleIn&& tin) {
  auto indexes = std::make_index_sequence< std::tuple_size<std::remove_reference_t<TupleIn>>{} >;
  return details::tuple_view( indexes, std::forward<TupleIn>(tin) );
}

并完成了?

将更多代码移至样板:

template<std::size_t I>
using index_t = std::integral_constant< std::size_t, I >;
template<std::size_t I>
constexpr index_t<I> index{};

template<std::size_t...Is>
auto unpack_indexes( std::index_sequence<Is...> ) {
  return [](auto&& f) {
    return decltype(f)(f)( index<Is>... );
  };
}
template<std::size_t N>
auto unpack_indexes( index_t<N> ) {
  return unpack_indexes( std::make_index_sequence<N>{} );
};

template<class TupleIn>
auto tuple_view(TupleIn&& tin) {
  auto size = std::tuple_size<std::remove_reference_t<TupleIn>>{};
  auto indexes = unpack_indexes(size);
  return indexes(
    [&](auto...Is) {
      return std::forward_as_tuple( std::get<Is>(std::forward<TupleIn>(tin))... );
    }
  );
}

做了一堆(可重用的)工作来摆脱details命名空间。

编写代码来操作索引应该与制作视图正交。

答案 1 :(得分:3)

太复杂了。 std::tie几乎可以为你完成所有工作。

#include <tuple>
#include <iostream>


template<class Tuple, std::size_t...Is>
auto tuple_view(Tuple&& t, std::index_sequence<Is...>)
{
    return std::tie(std::get<Is>(std::forward<Tuple>(t))...);
}

int main()
{
    auto a = std::make_tuple(1,2,3);

    auto b = tuple_view(a, std::index_sequence<0,1,2,1,0>());

    std::cout << std::get<0>(b) << std::endl;
    std::cout << std::get<1>(b) << std::endl;
    std::cout << std::get<2>(b) << std::endl;
    std::cout << std::get<3>(b) << std::endl;
    std::cout << std::get<4>(b) << std::endl;
}

预期产出:

1
2
3
2
1