I'm trying to implement a way of removing some types from a tuple; for instance I want to be able to e.g. only take a tuple of the first 2 template arguments for a tuple depending on a condition:
#include <cstdint>
#include <tuple>
template <typename... tpl> struct Helper {
template <std::size_t rem, typename curr, typename... rest> struct take {
using type = Helper<(tpl..., curr)>::take<rem-1, rest...>::type; // here, I'm trying (2.)
};
template <typename curr, typename... rest> struct take<0, curr, rest...> {
using type = std::tuple<tpl...>;
};
};
template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;
int main() {
take_t<2, int, int, int> k = std::make_tuple(1, 2);
}
edit The line Helper fails with the following message:
/home/juli/test.cc:6:18: error: need ‘typename’ before ‘Helper<tpl ..., curr>::take’ because ‘Helper<tpl ..., curr>’ is a dependent scope
6 | using type = Helper<tpl..., curr>::take<rem-1, rest...>::type;
and when I provide typename
/home/juli/test.cc:6:53: error: expected ‘;’ before ‘<’ token
6 | using type = typename Helper<tpl..., curr>::take<rem-1, rest...>::type;
edit2 I achieved this via [helper functions](https://gist.github.com/juliusHuelsmann/669f537aeb5e7105386d510d186b24e1 ), but those fail with non primitive types when the constructor is not constexpr so I cannot use it in my use case and am curious to know how to achieve this and why my approach failed.
The answer to Question 2)
Yes it is possible, as @user253751 suggested, my original approach (Helper<tpl..., curr>
) is correct, I made a different error though, which caused the aforementioned error in that line by omitting 'template' after the used type.
The code below shows the fix and works fine:
#include <cstdint>
#include <tuple>
template <typename... tpl> struct Helper {
template <std::size_t rem, typename curr, typename... rest> struct take {
using tp = Helper<tpl..., curr>;
using type = tp::template take<rem-1, rest...>::type;
};
template <typename curr, typename... rest> struct take<0, curr, rest...> {
using type = std::tuple<tpl...>;
};
};
template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;
int main() {
take_t<2, int, int, int>::type k = std::make_tuple(1, 2);
}
The answer to Question 1 is given by the trick performed here, see a full example here. It is possible e.g. by the following construct (adapted from the example on-the-fly and untested):
template <class tuple> struct foo;
template <class ... args> struct foo<std::tuple<args...> {
// implementation here!
};
which is very neat and can be used for integer_sequence aswell (which I tried and did not succeed to do until now).
An entire implementation of what I wanted to achieve that is much clearer than my initial approach above can be found here:
template <size_t s, typename is, typename ...args> struct thlp;
template <size_t s, size_t... i, class... args> struct thlp<s, std::index_sequence<i...>, args...> {
static_assert(s <= sizeof...(args), "Requested new tuple size exceeds old one.");
using type = std::tuple<std::tuple_element_t<i, std::tuple<args...>>...>;
};
template <size_t s, typename... args>
using conditional_tuple_t = thlp<s, decltype(std::make_index_sequence<s>()), args...>::type;