c++templatestemplate-templates

Is it possible to write a C++ template function whose template arg works if-and-only-if it is template?


I'm trying to write a little template function that returns sequential integer id's for templates passed to it. The following works for many templates, but not for templates that take non-type arguments (like std::array). Is there any way to make this work more generally? If not, what is my best alternative? (I'm tempted to just trust the caller to only pass template template args, and force them to include some kind of pointless default template args so it's a normal type T: ttypeid<vector<void>>(), ttypeid<array<void, 0>>(), etc.)

#include <atomic>
#include <vector>
#include <map>
#include <array>
#include <iostream>

using namespace std;

inline unsigned gen_id() {
    static atomic<unsigned> id = 0;
    return ++id;
}

template <template<typename ...> typename T>
inline unsigned ttypeid() {
    static unsigned id = gen_id();h
    return id;
}

int main() {
    cout << ttypeid<vector>() << endl;
    cout << ttypeid<map>()    << endl;

    // cout << ttypeid<array>()  << endl;
    //
    // error: no matching function for call to
    //     ‘ttypeid<template<class _Tp, long unsigned int _Nm> struct std::array>()’

    return 0;
}

Solution

  • Unfortunately type and non-type template (variadic) arguments do not mix well.

    For std::array you can add this:

    template <template<typename, auto,typename...> typename T>
    inline unsigned ttypeid() {
        static unsigned id = gen_id();
        return id;
    }
    

    Live Demo

    Though, if you want to handle another template, lets say T<Type,int,Type,int,Type> you need to write another overload. The good thing is that non-type template arguments are not that abundant. In your own templates you can avoid them by wrapping values in a type instead of using the bare value (cf. eg. std::integral_constant).