Reputation: 10936
Given a container, for example a std::list<T>
or std::vector<T>
, I want to generate a new type std::list<NewT>
or std::vector<NewT>
respectively in a situation I do not know the container (std::list
or std::vector
) in advance.
My current (but failing) attempt looks as follows:
#include <list>
#include <vector>
template<class Cont, class NewT> struct SameContNewT : public std::false_type
{};
template<class T, class Alloc, class NewT>
struct SameContNewT<std::list<T, Alloc>, NewT>
{ typedef typename std::list<NewT> type; };
template<class T, class Alloc, class NewT>
struct SameContNewT<std::vector<T, Alloc>, NewT>
{ typedef typename std::vector<NewT> type; };
int main()
{
typedef std::list<int> IntList;
typedef std::list<float> FloatList;
typedef SameContNewT<IntList, float> FloatListToo;
static_assert(std::is_same<FloatListToo, FloatList>::value, "No.");
}
How can the desired behavior be achieved?
Upvotes: 1
Views: 107
Reputation: 275330
template<class Z, class...Ts>
struct rebind_types;
template<class Z, class...Ts>
using rebind_types_t=typename rebind_types<Z,Ts...>;
template<template<class...>class Z, class...Us, class...Ts>
struct rebind_types<Z<Us...>, Ts...>{
using type=Z<Ts...>;
};
now we get:
typedef std::list<int> IntList;
typedef std::list<float> FloatList;
typedef rebind_types_t<IntList, float> FloatListToo;
static_assert(std::is_same<FloatListToo, FloatList>::value, "No.");
and we can pass more than one type argument to rebind_types_t
. We "gut" the template instance passed, and give it new arguments.
For a container-specific version, you'd have to understand what parameters where allocators etc (allocators have ways to be rebound to new types). And dealing with things like comparators and hashes for associative containers get tricky.
Upvotes: 1
Reputation: 63124
Some template template parameter twiddling later...
template <class T, class NewP>
struct SameContNewT;
template <template <class...> class T, class... TPs, class NewP>
struct SameContNewT<T<TPs...>, NewP> {
using type = T<NewP>;
};
This does throw away all template arguments of the original type, such as allocators for standard containers. Something more involved would be needed for these, since you can't reuse T
's allocator, which is most probably std::allocator<typename T::value_type>
and is not fit for allocating NewT
's.
Upvotes: 3
Reputation: 153810
The type you want isn't SameContNewT<IntList, float>
but its nested type type
. You can fix that directly using
typedef SameContNewT<IntList, float>::type FloatListToo;
... or using an alias template:
template <typename Cont, typename New>
using SameContNew = typename SameContNewT<Cont, New>::type;
... and dropping the T
.
If you want to get fancy and do the trick for a mostly arbitrary container, you can also use something like that:
template <template <typename...> class Cont, typename... T, typename New>
struct SameContNewT<Cont<T...>, New>
{ typedef Cont<New> type; };
However, using this specialization just makes the approach more general. It doesn't fix the original problem.
Upvotes: 3