Search code examples
c++templatesc++20template-meta-programming

How can I replace a type in a template at any level of nesting?


I have a type named the_bad. I want to replace that type with the_good using a template metafunction. I expect that template metafunction to be quite complex so let's call it the_ugly.

My problem is that the_bad could be nested in other templates, and I want to leave the other templates and parameters untouched. Here's an example of what I mean, with an incomplete implementation of the_ugly:

struct the_good {};
struct the_bad {};

template<typename T>
struct the_ugly_impl {
    using type = T;
};

template<>
struct the_ugly_impl<the_bad> {
    using type = the_good;
};

template<typename T>
using the_ugly = typename the_ugly_impl<T>::type;

// passes, yay!
static_assert(std::same_as<the_good, the_ugly<the_bad>>);

// doesn't pass
static_assert(std::same_as<std::vector<the_good>, the_ugly<std::vector<the_bad>>>);

// doesn't pass
static_assert(std::same_as<std::list<std::vector<the_good>>, the_ugly<std::list<std::vector<the_bad>>>>);

How can I fix the_ugly so that it replaces the_bad to the_good at any nesting level, while keeping all other template and other template parameters untouched?


Solution

  • You just need to handle the template case, and have that invoke itself recursively:

    template<typename T>
    struct the_ugly_impl {
        using type = T;
    };
    
    template<typename T>
    using the_ugly = typename the_ugly_impl<T>::type;
    
    template<>
    struct the_ugly_impl<the_bad> {
        using type = the_good;
    };
    
    template <template <typename...> class L, typename... Ts>
    struct the_ugly_impl<L<Ts...>> {
        using type = L<the_ugly<Ts>...>;
    };