I”m pretty sure this is impossible to do in the general case, before C++26.
## A partial solution
There are several ways to do this, depending on exactly what you want to do.
If all you want to know is if `T` is some specialization of `std::vector`, then you could use a simple meta-function like this:
```c++
template<template<typename...> typename T, typename U>
inline constexpr auto is_specialization_of = false;
template<template <typename...> typename T, typename... Args>
inline constexpr auto is_specialization_of<T, T<Args...>> = true;
```
[Example usage](https://compiler-explorer.com/z/oo9ns7EKG):
```c++
static_assert(is_specialization_of<std::vector, std::vector<int>>);
static_assert(is_specialization_of<std::vector, std::vector<double>>);
static_assert(is_specialization_of<std::vector, std::vector<std::vector<int>>>);
static_assert(is_specialization_of<std::list, std::list<int>>);
static_assert(is_specialization_of<std::list, std::list<double>>);
static_assert(is_specialization_of<std::list, std::list<std::vector<int>>>);
static_assert(not is_specialization_of<std::vector, int>);
static_assert(not is_specialization_of<std::vector, std::list<int>>);
static_assert(not is_specialization_of<std::vector, std::list<std::vector<int>>>);
```
That will work at least as far back as C++14 (though the `inline` specifier requires C++17). If, instead of variable templates, you use a class template with a constant `value`, like `std::bool_constant`, then you could even do it in C++11.
If you want to *replace* the template parameters with new ones—for example, to turn a `std::vector<int>` into a `std::vector<double>`—then you could do something like:
```c++
template<typename T, typename... Args>
struct rebind_with_;
template<template<typename...> typename T, typename... Ts, typename... Args>
struct rebind_with_<T<Ts...>, Args...>
{
using type = T<Args...>;
};
template<typename T, typename... Args>
using rebind_with = rebind_with_<T, Args...>::type;
```
[Example usage](https://compiler-explorer.com/z/YnbGrx777):
```c++
static_assert(std::is_same_v<
rebind_with<std::vector<int>, double>,
std::vector<double>
>);
static_assert(std::is_same_v<
rebind_with<std::vector<int>, std::vector<int>>,
std::vector<std::vector<int>>
>);
static_assert(std::is_same_v<
rebind_with<std::map<int, char>, double, float>,
std::map<double, float>
>);
```
And finally, if you want to replace the template but keep the parameters—for example, to turn a `std::vector<int>` into a `std::list<int>`—you could use basically the same technique:
```c++
template<template<typename...> typename T, typename U>
struct rebind_as_;
template<template<typename...> typename T, template<typename...> typename U, typename... Args>
struct rebind_as_<T, U<Args...>>
{
using type = T<Args...>;
};
template<template<typename...> typename T, typename U>
using rebind_as = rebind_as_<T, U>::type;
```
[Example usage](https://compiler-explorer.com/z/hh3ea8KE7):
```c++
static_assert(std::is_same_v<
rebind_as<std::list, std::vector<int>>,
std::list<int>
>);
static_assert(std::is_same_v<
rebind_as<std::multimap, std::map<int, double>>,
std::multimap<int, double>
>);
```
All of these solutions work, but only with templates that only have type parameters. As soon as you throw a non-type template parameter or template-template parameter (or anything else) into the mix, everything breaks. For example, none of these will work with `std::array`:
```c++
// Will not compile:
static_assert(is_specialization_of<std::array, std::array<int, 2>>);
// Will not compile:
using type = rebind_with<std::array<int, 2>, double, 4>; // expected: std::array<double, 4>
```
There was a proposal in the works for [universal template parameters](https://wg21.link/p2989), which would solve the problem. That may come eventually, but as of C++26, the only solution we have is reflection.
## The reflection solution
With reflection, a complete solution is possible. Indeed, some of the meta-functions we need are already included.
If you want to get the template of a type, that’s *basically* just `template_of(^^T)`. If you want to know is if `T` is some specialization of `std::vector`, then what you want is *basically* `template_of(^^T) == ^^std::vector`.
There is one little catch, though. If `T` is an alias, then even if it is *aliasing* a template, it may not itself *be* a template.
Consider `std::string`. `std::string` is not a template; it has no template arguments. But `std::string` is an alias for `std::basic_string<char>` (plus some other defaulted template parameters), and `std::basic_string` *is* a template, with template parameters (`char`, and a couple of others. If you just did `template_of(^^std::string)`, you would get an error… because `std::string` is not a template. You have to dealias it first; `template_of(dealias(^^std::string)) == ^^std::basic_string` would work.
You would have to decide what you *really* want to happen. Reflection gives you so freaking much power, that there are just so many possibilities. I can only guess that what you *probably* want is `has_template_arguments(dealias(^^T)) and template_of(dealias(^^T)) == ^^std::vector` ([example](https://compiler-explorer.com/z/58on7nhej)). But without knowing what you really want this for, it’s just a guess.
And, of course, for the other possibilities I mentioned, writing meta-functions with reflection is pretty trivial. Again, though, since reflection comes with so much more power, there are many more possibilities for what might be “correct”, so it really depends on your exact need. But here is one set of possibilities:
```c++
consteval auto is_specialization_of(std::meta::info tmpl, std::meta::info type)
{
return has_template_arguments(type) and template_of(type) == tmpl;
}
consteval auto rebind_with(std::meta::info t, std::same_as<std::meta::info> auto... params)
{
return substitute(template_of(t), std::vector{params...});
}
consteval auto rebind_as(std::meta::info new_template, std::meta::info type)
{
return substitute(new_template, template_arguments_of(type));
}
```
[Check Compiler Explorer for usage examples](https://compiler-explorer.com/z/MGPGf3o99).