Search code examples
c++c++17template-argument-deductiontemplate-templates

"decay" a templated alias


This is a followup to this question.

I'm having a templated type with a template template argument

template <template <typename...> class CONTAINER, typename NUMBERTYPE>
struct spam {
    template <class T>
    using Temp = CONTAINER<T>;
};

I want to write a (templated) function that takes a spam instance and returns a spam instance of a slightly different type. I want to maintain the CONTAINER template parameter from the input and only specify the NUMBERTYPE. (godbolt link)

#include <type_traits>
#include <vector>

template <template <typename...> class CONTAINER, typename NUMBERTYPE>
struct spam {
    template <class T>
    using Temp = CONTAINER<T>;
};

template <typename T>
auto function(T in) {
    spam<T::template Temp, double> retval;
    return retval;
}

int main() {
    spam<std::vector, float> one;
    // spam<std::vector, double> two = function(one);
    auto two = function(one);
    return 0;
}

This mostly works, but I wanted to check if function() returns the type I expected by receiving the expected spam<std::vector, double> rather than accepting an auto return. The non-auto version does not compile because

<source>:18:45: error: conversion from 'spam<spam<std::vector, float>::Temp,[...]>' to non-scalar type 'spam<std::vector,[...]>' requested

     spam<std::vector, double> two = function(one);

                                     ~~~~~~~~^~~~~

i.e. I have a mismatch between spam<std::vector, double> and spam<spam<std::vector, float>::template Temp, doulbe>, although I expect that spam<std::vector, float>::template Temp is the same as std::vector. (In fact I can check that std::is_same_v<spam<std::vector, float>::template Temp<int>, std::vector<int>> is indeed true - i.e. after providing the template arguments to Temp I have expected/desired behaviour, just that the code I'm contributing to works mostly with the unresolved CONTAINER).

QUESTION: is there a way to normalize T::template Temp to whatever it is and remove the spam<...>::Temp from the type?


Solution

  • QUESTION: is there a way to normalize T::template Temp to whatever it is and remove the spam<...>::Temp from the type?

    No, as far I know.

    But isn't necessary, in this case, because you can rewrite function() to intercept the template-template parameter CONTAINER.

    I mean: you can rewrite function() as follows

    template <template <typename...> class C, typename N>
    spam<C, double> function (spam<C, N> const &)
     { return {}; }
    

    The following is a full compiling example

    #include <type_traits>
    #include <vector>
    
    template <template <typename...> class CONTAINER, typename NUMBERTYPE>
    struct spam
     {
       template <typename T>
       using Temp = CONTAINER<T>;
     };
    
    template <template <typename...> class C, typename N>
    spam<C, double> function (spam<C, N> const &)
     { return {}; }
    
    int main() {
        spam<std::vector, float> one;
        spam<std::vector, double> two = function(one);
        auto three = function(one);
    }