开发者

implementing a variadic zip function with const-correctness

I'm trying to implement a zip function. zip's parameters are each wrapped<Ti>, where Ti varies from parameter to parameter.

zip takes these wrapped<Ti>s and produces a wrapped<tuple<T1&,T2&,...TN&>>, or in other words a wrapped tuple of references to its parameters. The references should preserve const-ness.

Here's my first stab at zip with one parameter, which doesn't work in general:

#include <utility>
#include <tuple>

// implement forward_as_tuple as it is missing on my system
namespace ns
{

template<typename... Types>
  std::tuple<Types&&...>
    forward_as_tuple(Types&&... t)
{
  return std::tuple<Types&&...>(std::forward<Types开发者_开发知识库>(t)...);
}

}

template<typename T>
  struct wrapped
{
  wrapped(T &&x)
    : m_x(std::forward<T>(x))
  {}

  T m_x;
};

template<typename T>
  wrapped<std::tuple<T&&>>
    zip(wrapped<T> &&x)
{
  auto t = ns::forward_as_tuple(std::forward<T>(x.m_x));
  return wrapped<std::tuple<T&&>>(t);
}

int main()
{
  wrapped<int> w1(13);

  wrapped<int> &ref_w1 = w1;

  // OK
  zip(ref_w1);

  const wrapped<int> &cref_w1 = w1;

  // XXX won't compile when passing a const reference
  zip(cref_w1);

  return 0;
}

Is there a way to implement the general, variadic case with a single version of zip?


Admittedly, I don't have a C++0x compiler that handles variadic templates, so I can't test it. But this might do the trick.

template<typename T>
    struct wrapped
{
    wrapped(T &&x)
    : m_x(std::forward<T>(x))
    {}

    typedef T type;

    T m_x;
};

template<typename... Types>
    wrapped<std::tuple<Types&&...>> zip(wrapped<Types>&&... x)
{
    return wrapped<std::tuple<Types&&...>>(std::tuple<Types&&...>(std::forward<Types>(x.m_x)...));
}

I'm not entirely sure if it is legal to call zip like this:

zip(wrapped<T1>(value1), wrapped<T2>(value2));

You may have to explicitly qualify the call:

zip<T1, T2>(wrapped<T1>(value1), wrapped<T2>(value2));


Here's the solution I arrived at:

#include <utility>
#include <tuple>
#include <cassert>

template<typename T>
  struct wrapped
{
  wrapped(T &&x)
    : m_x(std::forward<T>(x))
  {}

  T m_x;
};

template<typename Tuple>
  wrapped<Tuple> make_wrapped_tuple(Tuple &&x)
{
  return wrapped<Tuple>(std::forward<Tuple>(x));
}

template<typename... WrappedTypes>
  decltype(make_wrapped_tuple(std::forward_as_tuple(std::declval<WrappedTypes>().m_x...)))
    zip(WrappedTypes&&... x)
{
  return make_wrapped_tuple(std::forward_as_tuple(x.m_x...));
}

int main()
{
  wrapped<int> w1(1);
  wrapped<int> w2(2);
  wrapped<int> w3(3);
  wrapped<int> w4(4);

  auto z1 = zip(w1,w2,w3,w4);

  z1.m_x = std::make_tuple(11,22,33,44);

  assert(w1.m_x == 11);
  assert(w2.m_x == 22);
  assert(w3.m_x == 33);
  assert(w4.m_x == 44);

  const wrapped<int> &cref_w1 = w1;

  auto z2 = zip(cref_w1, w2, w3, w4);

  // does not compile, as desired
  // z2.m_x = std::make_tuple(111,222,333,444);

  return 0;
}

Having zip take WrappedTypes... instead of wrapped<T>... isn't as satisfying a solution, but it works.


template<typename T>
    struct wrapped
{
    wrapped(T &&x)
    : m_x(std::forward<T>(x))
    {}

    typedef T type;

    T m_x;
};

template<typename... Types>
    wrapped<std::tuple<T&&...>> zip(wrapped<Types>... &&x)
{
    return G+
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜