Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a type be removed from a template parameter pack?

I'm searching for a way to remove (let's say for now all occurences of) a type from a template parameter pack. The end result would be a struct that looked like this :

template<typename T, typename...Ts>
struct RemoveT
{
    using type = /* a new type out of Ts that does not contain T */
}

Let's say that the marginal case RemoveT<int, int> would be handled by returning void (not handled in the code that follows). My initial design looks like this:

// --------------------------------------------------------------
// 1. A "way" of typedefing variadic number of types ------------
template<typename...Ts>
struct pack { 
    using type = Ts; 
};
// --------------------------------------------------------------

// --------------------------------------------------------------
template<typename T, typename...Ts> struct RemoveT;

template<typename T, typename T1, typename...Ts>
struct RemoveT {
    using type = typename pack<T1, typename RemoveT<T, Ts...>::type>::type;
};

template<typename T, typename T1>
struct RemoveT<T, T1> { 
    using type = T1; 
};

template<typename T, typename...Ts>
struct RemoveT<T, T, Ts...> {
    using type = typename RemoveT<Ts...>::type;
};
// --------------------------------------------------------------

Now I can't even begin to test this code because the pack structure is not valid C++

Reiteration

Just in case this is helpfull for an answer, some other thoughs on solving it

  • One could argue that pack is not even useful at all. We could instead move around the RemoveT structure, creating a new RemoveT that only contains the types needed. The problem then transforms in extracting the types out of the struct
  • We could create type pairs that mimic the behaviour of typelists and take a more recursive approach on this.

Bottom Line

For variadic types Ts and a type T: Can I create Us out of Ts ommiting T ?

like image 810
Nikos Athanasiou Avatar asked May 25 '14 13:05

Nikos Athanasiou


People also ask

What is type template parameter?

The first template parameter is the type of elements the container stores and the second template parameter is the defaulted allocator a container of the standard template library has. Even the allocator has a default value such as in the case of a std::vector. The allocator depends on the type of elements.

How many kinds of template parameters are there in C++?

The template name must be unique in its scope (except for overloaded functions). There are three kinds of template parameters: values, types, and class templates.

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)


3 Answers

The following provides a non-recursive and direct way to remove T from Ts... and, like Jarod42's solutions, yields a std::tuple<Us...> but without the need to use typename ...::type:

#include <tuple>
#include <type_traits>

template<typename...Ts>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));

template<typename T, typename...Ts>
using remove_t = tuple_cat_t<
    typename std::conditional<
        std::is_same<T, Ts>::value,
        std::tuple<>,
        std::tuple<Ts>
    >::type...
>;


int main()
{
    static_assert(std::is_same<
        remove_t<int, int, char, int, float, int>,
        std::tuple<char, float>
    >::value, "Oops");
}

Live example

like image 92
Daniel Frey Avatar answered Nov 12 '22 07:11

Daniel Frey


Following may help:

namespace detail
{
    template <typename T, typename Tuple, typename Res = std::tuple<>>
    struct removeT_helper;


    template<typename T, typename Res>
    struct removeT_helper<T, std::tuple<>, Res>
    {
        using type = Res;
    };

    template<typename T, typename... Ts, typename... TRes>
    struct removeT_helper<T, std::tuple<T, Ts...>, std::tuple<TRes...>> :
        removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes...>>
    {};

    template<typename T, typename T1, typename ...Ts, typename... TRes>
    struct removeT_helper<T, std::tuple<T1, Ts...>, std::tuple<TRes...>> :
        removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes..., T1>>
    {};

}

template <typename T, typename...Ts> struct RemoveT
{
    using type = typename detail::removeT_helper<T, std::tuple<Ts...>>::type;
};

static_assert(std::is_same<std::tuple<char, float>,
                        typename RemoveT<int, int, char, int, float, int>::type>::value, "");
like image 44
Jarod42 Avatar answered Nov 12 '22 07:11

Jarod42


First, move all the specific template names out into a list. There might be a way of specifying a template name, and a list of parameters, giving that template with the parameters, but I haven't been able to figure it out:

template <typename...TArgs> struct TypeList
{
    typedef std::tuple<TArgs...> tuple_type;
    // whatever other types you need
};

Next, define addition:

template<typename T, typename TList> struct AddT;

template<typename T, typename ... TArgs>
struct AddT< T, TypeList<TArgs...> >
{
    typedef TypeList<T, TArgs... > type;
};

Then, define removal:

template<typename R, typename ... TArgs> struct RemoveT;

template<typename R>
struct RemoveT<R>
{
    typedef TypeList<> type;
};

template<typename R, typename T, typename ...TArgs>
struct RemoveT<R, T, TArgs...>
{
    typedef typename std::conditional
        < std::is_same<R, T>::value
        , typename RemoveT<R, TArgs...>::type
        , typename AddT<T, typename RemoveT<R, TArgs...>::type>::type
        >::type type;
};

Finally, test:

int result = 0;
result = std::is_same
    < std::tuple<long,double>
    , RemoveT<int, int, long, int, double, int>::type::tuple_type
    >::value;
assert ( result );
like image 3
KevinZ Avatar answered Nov 12 '22 07:11

KevinZ