Doing a static_assert that a template type is another template
How do I static_assert
like this? Maybe Boost supports it if not C++ or new features in C++11?
template<T>
struct foo {};
template<FooType>
struct bar {
static_assert(FooType is indeed foo<T> for some T,"failure"); //how?
};
Solution 1:
You could do something along these lines. Given a trait that can verify whether a class is an instantiation of a class template:
#include <type_traits>
template<typename T, template<typename> class TT>
struct is_instantiation_of : std::false_type { };
template<typename T, template<typename> class TT>
struct is_instantiation_of<TT<T>, TT> : std::true_type { };
Use it as follows in your program:
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<FooType, foo>::value, "failure");
};
int main()
{
bar<int> b; // ERROR!
bar<foo<int>> b; // OK!
}
If you want, you could generalize this to detect whether a class is an instance of a template with any number of (type) parameters, like so:
#include <type_traits>
template<template<typename...> class TT, typename T>
struct is_instantiation_of : std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : std::true_type { };
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo, FooType>::value, "failure");
};
You would then use it this way in your program:
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo, FooType>::value, "failure");
};
int main()
{
bar<int> b; // ERROR!
bar<foo<int>> b; // OK!
}
Here is a live example.
Solution 2:
Some small improvements over the other answers:
- the name actually makes sense regarding the order of the parameters
- handles
const
,volatile
, and reference types properly via std::decay - implements C++14-style _v
constexpr
variable - accepts an arbitrary number of types to test against (tests for ALL)
I have intentionally not put the std::decay_t on the is_template_for_v because a type trait should work identically regardless of whether it is called with the _v suffix or not.
This does require C++17 for std::conjunction
, but you can either remove the variadic feature or implement your own conjunction
using c++11/14.
template<template<class...> class tmpl, typename T>
struct _is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct _is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
Usage:
static_assert(is_template_for_v<std::vector, std::vector<int>>); // doesn't fire