1

I want a function I'm writing to return a tuple of N foos for some value N (N is not part of the code).

If it were C++14 I could probably get around actually declaring anything and just use auto as the return type, but that won't work in C++11. I don't want to use -> auto decltype because that would mean a lot of text, and I might as well write, say, std::tuple{foo, foo, foo, foo}.

Another alternative is having the return type be an std::array<foo, N>; and this should probably work. What I'm wondering, though, is whether I can get by without pulling in all of <array>, whose functionality I don't actually need.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 3
    Tuples are generally chosen when you want an ordered collection of *different* types. An array is N foos, and is much simpler to use – Caleth Dec 08 '16 at 22:56

4 Answers4

4
#include <tuple>
#include <utility>
#include <iostream>

namespace detail {

// the concept of N Ts. This is a type generator.
template<class T, std::size_t N>
struct tuple_of_n
{
  using rest_tuple = typename tuple_of_n<T, N-1>::type;
  using type = decltype(std::tuple_cat(std::declval<std::tuple<T>>(), std::declval<rest_tuple>()));
};

// terminal case where N is 0    
template<class T>
struct tuple_of_n<T, 0>
{
  using type = std::tuple<>;
};
}

// typedef turns the generator into a type
template<class T, std::size_t N>
  using tuple_of_n = typename detail::tuple_of_n<T, N>::type;

int main() {

  // test
  using two_ints = tuple_of_n<int, 2>;

  // proof of equivalency
  two_ints is = std::make_tuple(2, 4);

  std::cout << std::get<0>(is) << std::endl;
  std::cout << std::get<1>(is) << std::endl;

  return 0;
}
jaggedSpire
  • 4,423
  • 2
  • 26
  • 52
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
3

The c++11 approach styling a bit to c++14, using external implemenation of integer_sequence [integer_seq.h] could look as follows:

#include <tuple>
#include <utility>
#include <iostream>
#include <initializer_list>
#include "integer_seq.h"

using namespace redi;


template <class T, std::size_t N, class = make_index_sequence<N>>
struct tuple_generator;

template <std::size_t, class T>
using typer = T;

template <class T, std::size_t N, std::size_t... Is>
struct tuple_generator<T, N, index_sequence<Is...>> {
    using type = std::tuple<typer<Is, T>...>;
};

int main() {
    static_assert(std::is_same<tuple_generator<int, 3>::type, std::tuple<int, int, int>>::value, "!");
}

[live demo]

W.F.
  • 13,888
  • 2
  • 34
  • 81
  • 1
    You should probably just include an available public implementation index sequence for C++11, which seem to be available e.g. on GitHub. – einpoklum Dec 09 '16 at 07:51
  • You're right - this would probably make the code much clearer – W.F. Dec 09 '16 at 07:55
2

I'm going to recycle the idea from @Smeeheey 's answer to a different question which I asked: C++ parameter pack, constrained to have instances of a single type?

The connection is, your question hasn't got much to do with tuple, it's really,

How do I make a parameter pack which contains the same type repeated some number of times?

And the most straightforward answer is, use std::make_index_sequence and a pack expansion:

#include <tuple>
#include <utility>

template <typename T, unsigned n>
struct repeat_tuple {
  template <unsigned>
  struct blah {
    using type = T;
  };

  template <typename IS>
  struct helper;

  template <unsigned ... Is>
  struct helper<std::integer_sequence<unsigned, Is...>> {
    using type = std::tuple<typename blah<Is>::type...>;
  };

  using type = typename helper<std::make_integer_sequence<unsigned, n>>::type;
};

template <typename T, unsigned n>
using repeat_tuple_t = typename repeat_tuple<T, n>::type;

static_assert(std::is_same<repeat_tuple_t<int, 3>, std::tuple<int, int, int>>::value, "");

int main() {}

This is ultimately the same as W.F.'s answer, but maybe a little terser. Edit: I guess he is backporting C++14 traits in his answer.

This approach works for things besides tuple as well, unlike the tuple_cat approach.

In fact I guess std::tuple could be a template template parameter here ...

:evil eyes:

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
-1

This is not possible with regular function.

A function's return type is fixed and can't change. Tuple with different # of types is each a different type so you can't have a single function that returns different tuples type.

example: tuple<int, int> is a different type compared to tuple<int,int,int>

Having said that, you could do this with function template (make_shared is implemented this way) with caller explicitly specifying return tuple type expected while making the function call. I don't think this is good usability as tuples are verbose.

Using std::array (or std::vector) is likely the easier route.

Chintan
  • 374
  • 2
  • 9