Home > Blockchain >  Giving an arbitrary container, deduce a container type of a related type
Giving an arbitrary container, deduce a container type of a related type

Time:10-05

Say I have a templated class Wrapper, is there a way to create a type alias template that automatically deduce a container of Wrapper <T> from a container of T, so that:

  • alias(Wrapper, vector<int>) would become vector<Wrapper<int>>
  • alias(Wrapper, map<int, string>) would become map<Wrapper<int>, Wrapper<string>>
  • alias(Wrapper, array<int, 10>) would become array<Wrapper<int>, 10>

So far the best attempt I've got is:

template<template<typename> typename U, template<typename...> typename Container, typename ...T>
using alias = std::remove_cvref_t<decltype(std::declval<Container<U<T>...>>())>;

However there are two problems:

  1. It must be called with syntax like:This version need to be called like(which is not ideal): alias(vector, Type) and alias(map, Key, Value). I would love to use alias(vector<Type>) and alias(map<Key, Value>) if possible.

  2. It is not compatible with std::array since the second template parameter of array is size_t not a type. I guess I could create a second type alias and call the corresponding one based on the container type, but I would prefer not have to do that.

CodePudding user response:

Not sure if this is exactly what you need, but specialization of class template can handle this nicely:

#include <type_traits>
#include <vector>
#include <array>
#include <map>
#include <string>

template<template<typename> typename Wrapper, typename Container>
struct repack;

template<template<typename> typename Wrapper, typename ValueT>
struct repack<Wrapper, std::vector<ValueT>>
{
    using type = std::vector<Wrapper<ValueT>>;
};

template<template<typename> typename Wrapper, typename ValueT, std::size_t N>
struct repack<Wrapper, std::array<ValueT, N>>
{
    using type = std::array<Wrapper<ValueT>, N>;
};

template<template<typename> typename Wrapper, typename Key, typename Value>
struct repack<Wrapper, std::map<Key, Value>>
{
    using type = std::map<Wrapper<Key>, Wrapper<Value>>;
};

template<template<typename> typename Wrapper, typename Container>
using repack_t = typename repack<Wrapper, Container>::type;

https://godbolt.org/z/naz9v48vb

It passes tests specified by you.

CodePudding user response:

There are no ways currently to be generic and mix non-type-template-parameters with type-template-parameter or template-template-parameter.

Then for your alias restricted to type-template-parameter, you might do something like:

template <template <typename> class Inner, typename Container>
struct alias;

template <template <typename> class Inner,
          template <typename...> class C,
          typename... Ts>
struct alias<Inner, C<Ts...>>
{
    using type = C<Inner<Ts>...>;
};

template <template <typename> class Inner, typename Container>
using alias_t = typename alias<Inner, Container>::type;

usage would be

alias_t<Wrapper, vector<int>> and not alias(Wrapper, vector<int>).

But as mentioned in comment, the generic way will wrap all parameters, including the default ones (such as Allocator).

Selecting the specializations as Marek R showed might be more appropriate.

  • Related