Let's check if
struct Thing {
int foo(double, bool) {return 0;}
};
has the int foo(double, bool) member function during compile time. There are many ways of doing this, and most are just variations of others. Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here? I'm just trying to learn some new techniques with templates and SFINAE.
#include <iostream>
#include <type_traits>
// Using void_t (this includes using std::is_detected).
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_foo : std::false_type {};
template <typename T>
struct has_foo<T,
void_t<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>
> : std::true_type {};
// Using the ... default argument.
template <typename T>
struct hasfoo {
template <typename U>
static std::true_type test (decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*); // or 'decltype(static_cast<int>(std::declval<U>().foo(double{}, bool{})))*' works fine too.
template <typename>
static std::false_type test (...);
static constexpr bool value = decltype(test<T>(nullptr))::value;
};
// Overloads and trailing return types.
template <typename>
struct Helper : std::true_type {};
template <typename T>
auto helper(int) -> Helper<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>;
template <typename>
std::false_type helper(long);
template <typename T>
constexpr bool hasFoo() {return decltype(helper<T>(0))::value;}
// Comma operator (basically the same as the above).
template <typename T>
auto check(int) -> decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})), std::true_type{});
template <typename T>
std::false_type check(...);
template <typename T>
using HasFoo = decltype(check<T>(0));
// Member function pointer template parameter.
template <typename T>
struct Hasfoo {
template <typename U, int(U::*)(double, bool)>
struct Tag;
template <typename U>
static constexpr bool test (Tag<U, &U::foo>*) {return true;}
template <typename>
static constexpr bool test (...) {return false;}
static constexpr bool value = test<T>(nullptr);
};
// Tests
struct Thing {
int foo(double, bool) {return 0;}
};
int main() {
static_assert (has_foo<Thing>::value, "");
static_assert (hasfoo<Thing>::value, "");
static_assert (hasFoo<Thing>(), "");
static_assert (HasFoo<Thing>::value, "");
}
Edit: I just remembered an elegant and more general solution that Yakk gave to a different question quite a while ago (here is his actual typing, modified only to match the foo function):
namespace meta {
namespace details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, decltype((void)(std::declval<Z<Ts...>>())), Ts...>:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
}
template<class T>
using member_foo = decltype(static_cast<int(T::*)(double, bool)>(&T::foo));
template<class T>
using has_member_foo = meta::can_apply<member_foo, T>;
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It is based on the noexcept operator and a trivial using declaration (here named Type).
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
#include<utility>
template<typename T, bool>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, noexcept(std::declval<T>().f(42))>>: std::true_type {};
struct S { void f(int) {} };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<T>::value, "!");
}
This solution has a problem if compared to the others.
As an example, the struct below would pass the test as well:
struct R { int f(double) {} };
In other terms, as long as the function to be tested accepts one argument to the type of which 42 can be cast and no matter what's the return type, it is accepted.
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It is based on the sizeof operator and a trivial using declaration (here named Type).
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
#include<cstddef>
template<typename T, std::size_t>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, sizeof(static_cast<void(T::*)(int)>(&T::f))>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It doesn't exploit any unevaluated context. Instead, it relies on a trivial using declaration (here named Type) and that's all.
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
template<typename T, void(T::*)(int)>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, &T::f>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It is based on function template and overloading. If you know how tag dispatching works, it should be pretty straightforward to understand.
It follows a minimal, working example:
#include<type_traits>
#include<cstddef>
template<typename T, void(T::*)(int) = &T::f>
constexpr std::true_type f(int) { return {}; }
template<typename T>
constexpr std::false_type f(char) { return {}; }
template<typename T>
constexpr auto detect() { return f<T>(0); }
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(detect<S>(), "!");
static_assert(not detect<R>(), "!");
static_assert(not detect<T>(), "!");
}
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
If you can use C++14, another way of check if a class has a member variable is by means if template variables.
As an example:
template<typename T, typename = void>
constexpr bool has_foo = false;
template<typename T>
constexpr bool has_foo<T, decltype(std::declval<T>().foo(), void())> = true;
I guess it's probably one of the more compact solution at least.
It follows a minimal, working example:
#include<utility>
template<typename T, typename = void>
constexpr bool has_f = false;
template<typename T>
constexpr bool has_f<T, decltype(std::declval<T>().f(0), void())> = true;
struct S { void f(int) {} };
struct T {};
int main() {
static_assert(has_f<S>, "!");
static_assert(not has_f<T>, "!");
}
Related
I want to create a few template classes in order to assert certain type of data:
assertType { static constexpr bool v{false}; };
template<> struct assertType<int> { static constexpr bool v{true}; };
template<> struct assertType<bool> { static constexpr bool v{true}; };
template<>struct assertType<float> { static constexpr bool v{true}; };
template<>struct assertType<long> { static constexpr bool v{true}; };
However, I would like to do it programmatically, So I thought about defining a "list" of the supported types:
template<typename ...Types>
struct TypesList { };
static inline TypesList<int, bool, float, long> supportedTypes;
And have that "list" passed to another template that, by recursion, generates the "assertType" template for every type in the list. Something like:
template<typename ...Ts>
struct BuildTemplates { };
template<typename ...Ts>
struct BuildTemplates<TypesList<Ts...>> { };
BuildTemplates<supportedTypes> /* Build the templates for int, bool, float and long */
So I can use:
assertType<int>::v // v should be true
assertType<bool>::v // v should be true
assertType<float>::v // v should be true
assertType<long>::v // v should be true
Any other template whose type is not one of those, should have its v value set to false.
Is this possible?
Thanks in advance.
The answer mostly depends on the syntax you want to get. For example, you could do this:
#include <type_traits>
template<typename... Types> struct TypeList {};
using SupportedTypes = TypeList<int, bool, float, long>;
template<typename T>
struct assertType {
template<class... Types>
static constexpr bool contains(TypeList<Types...>) {
return (std::is_same_v<T, Types> || ...);
}
static constexpr bool v = contains(SupportedTypes{});
};
static_assert( assertType<int>::v);
static_assert( assertType<bool>::v);
static_assert(!assertType<short>::v);
If you can use boost, boost::mp11::mp_contains could be employed to make the implementation trivial:
#include <boost/mp11.hpp>
using SupportedTypes = boost::mp11::mp_list<int, bool, float, long>;
template<typename T>
struct assertType : boost::mp11::mp_contains<SupportedTypes, T> {};
static_assert( assertType<int>::value);
static_assert( assertType<bool>::value);
static_assert(!assertType<short>::value);
Or if you want to use v instead of value (which is de facto a standard name):
template<typename T>
struct assertType {
static constexpr bool v =
boost::mp11::mp_contains<SupportedTypes, T>::value;
};
This is only possible if your programmatic solution is allowed to define a template specialization of assertType, or to be used within its template definition.
Here's an approach by defining a single partial template specialization of assertType in c++11:
template <typename>
struct assertType { static constexpr bool v = false; };
template <typename...>
struct TypeList {};
using supportedTypes = TypesList<int, bool, float, long>;
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename U, typename... Us>
struct any_of<T, TypeList<U, Us...>> : any_of<T, TypeList<Us...>> {};
template <typename T, typename... Us>
struct any_of<T, TypeList<T, Us...>> : std::true_type {};
template <typename T>
struct assertType<typename std::enable_if<any_of<T, supportedTypes>, T>::type> {
static constexpr bool v = true;
};
You could also just use it within the class template definition itself rather than defining a partial specialization:
// assuming supportedTypes and any_of have already been defined
template <typename T>
struct assertType { static constexpr bool v = any_of<T, supportedTypes>::value; };
In c++17 the definition of any_of becomes much simpler with a fold expression and std::bool_constant:
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::bool_constant<(... || std::is_same_v<T, Us>)> {};
Or by using std::disjunction:
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::disjunction<std::is_same_v<T, Us>...> {};
Finally in c++20, you can simplify the definition of the assertType partial template specialization using concepts:
// assuming supportedTypes and any_of have already been defined
template <typename T, typename L>
concept any_of_v = any_of<T, L>::value;
template <typename>
struct assertType { static constexpr bool v = false; };
template <any_of_v<supportedTypes> T>
struct assertType<T> { static constexpr bool v = true; };
Except for instantiating a template or preprocessor trickery, there is no way to generate declarations in C++ automatically. Each declaration must be written out explicitly.
But there is no need to have an explicit specialization for each type in the list. You can simply use a single primary template for assertType and set the v member according to membership in the list or you can use a fixed number of partial specializations. The other answers give approaches to do this.
Say I was trying to implement a concept meowable that
Integral types are meowable.
Class types with member function meow are meowable. This is in the final target but the current question doesn't focus on it.
Tuple-like types with only meowable elements are meowable.
std::ranges::range with meowable elements are meowable. This is in the final target but the current question doesn't focus on it.
Then I came up with this implementation(simplified as I could):
#include <concepts>
#include <type_traits>
#include <ranges>
#include <utility>
#include <tuple>
template<class T>
concept meowable_builtin = std::integral<T>;
template<class T, std::size_t I>
concept has_tuple_element = requires (T t) {
typename std::tuple_element<I, T>::type;
{ get<I>(t) } -> std::convertible_to<std::tuple_element_t<I, T>&>;
};
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} &&
[]<std::size_t...I>(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{});
template<class T> struct is_meowable: std::false_type{};
template<meowable_builtin T> struct is_meowable<T>: std::true_type{};
template<tuple_like T>
struct is_meowable<T>
: std::bool_constant<
[]<std::size_t...I>(std::index_sequence<I...>) {
return (is_meowable<std::tuple_element_t<I, T>>::value && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{})
> {};
template<class T>
concept meowable_tuple = tuple_like<T> && is_meowable<T>::value;
template<class T>
concept meowable = is_meowable<T>::value;
static_assert(meowable<int>);
//static_assert(tuple_like<std::tuple<int>>);
static_assert(is_meowable<std::tuple<int>>::value);
But some compilers don't like it (https://godbolt.org/z/5vMTEhTdq):
1. GCC-12 and above: internal compiler error.
2. GCC-11: accepted.
3. Clang-13 and above: static_assert fired.
4. MSVC-v19: accepted.
However, if I uncomment the second last line of code, all compilers are happy. (Instantiation point of concepts?)
So my questions are:
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
How can I achieve my target?
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
This is apparently a bug of GCC-trunk and Clang-trunk, the issue here is that GCC/Clang doesn't properly handle the template partial specialization based on the concept initialized by the lambda. Reduced
template<class>
concept C = [] { return true; } ();
template<class T>
struct S {};
template<class T>
requires C<T>
struct S<T> { constexpr static bool value = true; };
// static_assert(C<int>);
static_assert(S<int>::value);
How can I achieve my target?
Replace lambda with the template function based on the reduced result
template<class T, std::size_t...I>
constexpr bool all_has_tuple_element(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
}
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} && all_has_tuple_element<T>(std::make_index_sequence<std::tuple_size_v<T>>{});
Demo
I wrote a GENERIC version (using SFINAE) with customizable predicate that will work with all the specialized tuple-like types (those that have specializations for std::tuple_element and std::tuple_size).
https://godbolt.org/z/Yf889GY6E
#include <cstddef>
#include <type_traits>
#include <tuple>
template <typename, typename = void>
struct is_complete : std::false_type {};
template <typename T>
struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};
// catching all standard (and specialized) tuple-like types
template <typename T>
struct is_tuple_like : is_complete<std::tuple_size<T>> {};
template <typename T, bool = is_tuple_like<T>::value>
struct get_tuple_size
{
constexpr static size_t value = 0;
};
template <typename T>
struct get_tuple_size<T, true>
{
constexpr static size_t value = std::tuple_size_v<T>;
};
// generic solution with predicate
template <template <typename> class PredicateT,
typename T,
bool = is_tuple_like<T>::value,
typename = decltype(std::make_index_sequence<get_tuple_size<T>::value>{})>
struct tuple_conjunction : PredicateT<T> {};
template <template <typename> class PredicateT,
typename T,
size_t ... I>
struct tuple_conjunction<PredicateT, T, true, std::index_sequence<I...>> :
std::conjunction<tuple_conjunction<PredicateT, std::tuple_element_t<I, T>>...> {};
////////////////////////////
template <typename T, typename = void>
struct has_meow : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
using meowable_pred = std::disjunction<std::is_integral<T>, has_meow<T>>;
template <typename T>
using meowable = tuple_conjunction<meowable_pred, T>;
#include <array>
struct cat
{
void mew() {}
};
struct dummy{};
int main()
{
static_assert(!is_complete<std::tuple_element<0, cat>>::value);
static_assert(is_complete<std::tuple_element<0, std::tuple<cat>>>::value);
static_assert(meowable<int>::value);
static_assert(meowable<cat>::value);
static_assert(!meowable<dummy>::value);
static_assert(meowable<std::tuple<long>>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
static_assert(!meowable<std::tuple<int, std::tuple<cat, long>, dummy>>::value);
// std::array
static_assert(meowable<std::array<cat, 42>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long, std::tuple<std::array<cat, 42>>>>>::value);
return 0;
};
With some adjustments it will also work with C++11.
You can provide any predicate you want for a recursive check. The example shows a usage with meowable_pred predicate.
OLD ANSWER
Here is a simple recursive SFINAE solution for C++17 (and 11 for some adjustments):
https://godbolt.org/z/cezqhb99b
#include <type_traits>
template <typename T, typename = void>
struct has_meow : std::false_type {};
// this is a customization point to deduce tuple-like traits
// you can define some default logic here,
// but for the sake of example let it be false by default
template <typename>
struct is_tuple_like : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
struct meowable : std::disjunction<std::is_integral<T>, has_meow<T>> {};
template <template <typename...> class TupleT, typename ... T>
struct meowable<TupleT<T...>> : std::conjunction<
is_tuple_like<TupleT<T...>>,
std::conjunction<meowable<T>...
> {};
#include <tuple>
#include <array>
// this will also catch std::pair
template <typename ... T>
struct is_tuple_like<std::tuple<T...>> : std::true_type {};
template <typename T, size_t N>
struct is_tuple_like<std::array<T, N>> : std::true_type {};
struct cat
{
void mew() {}
};
int main()
{
static_assert(meowable<int>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<cat>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
return 0;
};
So I'm very familiar with the paradigm of testing if a member function exists. Currently this code works:
#include <iostream>
#include <type_traits>
struct has_mem_func_foo_impl {
template <typename U, U>
struct chk { };
template <typename Class, typename Arg>
static std::true_type has_foo(chk<void(Class::*)(Arg), &Class::foo>*);
template <typename, typename>
static std::false_type has_foo(...);
};
template <typename Class, typename Arg>
struct has_mem_func_foo : decltype(has_mem_func_foo_impl::template has_foo<Class,Arg>(nullptr)) { };
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, int>::value, "bar has foo(int)" );
}
unfortunately if I make a slight adjustment:
#include <iostream>
#include <type_traits>
struct has_mem_func_foo_impl {
template <typename U, U>
struct chk { };
template <typename Class, typename... Arg>
static std::true_type has_foo(chk<void(Class::*)(Arg...), &Class::foo>*);
template <typename, typename...>
static std::false_type has_foo(...);
};
template <typename Class, typename... Arg>
struct has_mem_func_foo : decltype(has_mem_func_foo_impl::template has_foo<Class,Arg...>(nullptr)) { };
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, int>::value, "bar has foo(int)" );
}
my static assertion fails. I was under the impression that variadic template parameter packs are treated just the same when expanded into their places. Both gcc and clang produce a failed static assertion.
The real root of my question is thus, is this standard behavior? It also fails when testing for the presence of a variadic templated member function.
The problem I see is that Arg... being passed int is not enough. It would be valid for the compiler to add new args to the end of it.
Deducing what to add to the end of it from nullptr_t isn't possible, so the compiler says "I give up, not this case".
But we don't need to have Arg... in a deducable context for your trick to work:
#include <iostream>
#include <type_traits>
template<class Sig>
struct has_mem_func_foo_impl;
template<class R, class...Args>
struct has_mem_func_foo_impl<R(Args...)> {
template <typename U, U>
struct chk { };
template <typename Class>
static constexpr std::true_type has_foo(chk<R(Class::*)(Args...), &Class::foo>*) { return {}; }
template <typename>
static constexpr std::false_type has_foo(...) { return {}; }
};
template <typename Class, typename Sig>
struct has_mem_func_foo :
decltype(has_mem_func_foo_impl<Sig>::template has_foo<Class>(nullptr))
{};
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, void(int)>::value, "bar has foo(int)" );
}
we move the Args... to the class itself, then only pass in the type to the function. This blocks deduction, which makes nullptr conversion to the member function pointer doable, and things work again.
I also included some improved signature based syntax, which also means it supports return type matching.
Note that you may be asking the wrong question. You are asking if there is a member function with a particular signature: often what you want to know is if there is a member function that is invokable with a certain set of arguments, with a return type compatible with your return value.
namespace details {
template<class T, class Sig, class=void>
struct has_foo:std::false_type{};
template<class T, class R, class... Args>
struct has_foo<T, R(Args...),
typename std::enable_if<
std::is_convertible<
decltype(std::declval<T>().foo(std::declval<Args>()...)),
R
>::value
|| std::is_same<R, void>::value // all return types are compatible with void
// and, due to SFINAE, we can invoke T.foo(Args...) (otherwise previous clause fails)
>::type
>:std::true_type{};
}
template<class T, class Sig>
using has_foo = std::integral_constant<bool, details::has_foo<T, Sig>::value>;
which tries to invoke T.foo(int), and checks if the return value is compatible.
For fun, I made the type of has_foo actually be true_type or false_type, not inherited-from. I could just have:
template<class T, class Sig>
using has_foo = details::has_foo<T, Sig>;
if I didn't want that extra feature.
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?
};
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.
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
As someone else wrote,
template<typename T, template<typename...> class TT>
struct is_specialization_of : std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_specialization_of<TT<Ts...>, TT> : std::true_type { };
However, beware that this works only for template classes whose template parameters are all typenames! Presented with
typedef std::array<int, 42> MyArray;
static_assert(is_specialization_of<MyArray, std::array>::value, "");
it will simply fail to compile at all.
I believe C++11/C++14/C++17 currently have no way to deal with this limitation.
From a previous question:
Doing a static_assert that a template type is another template
Andy Prowl provided me with this code that allows me to static_assert that a template type is another template type:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //success
};
int main(int,char**)
{
bar<foo<int>> b;
return 0;
}
This works great.
But this does not work for a subclass of foo<whatever>:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //fail
};
//Added: Subclass of foo<int>
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; //Changed: Using the subclass
return 0;
}
Can Andy Prowl's is_instantiation_of code be extended to allow for subclasses?
As KerrekSB wrote in his answer, an equally general extension of the solution you posted cannot be achieved.
However, if it is OK for you to give up a bit of genericity, you can write a type trait specific for foo (exploiting the fact that derived-to-base is one of the few conversions that are performed during type deduction):
#include <type_traits>
template<typename T>
struct foo {};
template<typename T>
constexpr std::true_type test(foo<T> const&);
constexpr std::false_type test(...);
template<typename T>
struct is_instantiation_of_foo : public decltype(test(std::declval<T>())) { };
You would then use it like so:
template<typename FooType>
struct bar {
static_assert(is_instantiation_of_foo<FooType>::value, "");
};
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; // Will not fire
return 0;
}
Here is a live example.
This seems to work in many cases:
#include <iostream>
#include <utility>
template <typename T> struct foo { };
struct foo_sub : foo<int> { };
// A fallback function that will only be used if there is no other choice
template< template <typename> class X >
std::false_type isX(...)
{
return std::false_type();
}
// Our function which recognizes any type that is an instantiation of X or
// something derived from it.
template< template <typename> class X, typename T >
std::true_type isX(const X<T> &)
{
return std::true_type();
}
// Now we can make a template whose value member's type is based
// the return type of isX(t), where t is an instance of type T.
// Use std::declval to get a dummy instance of T.
template <template <typename> class X,typename T>
struct is_instantiation_of {
static decltype(isX<X>(std::declval<T>())) value;
};
template <typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value,"");
};
int main(int,char**)
{
//bar<int> a; // fails the static_assert
bar<foo<int>> b; // works
bar<foo_sub> c; // works
return 0;
}
As noted by Yakk, one place that it doesn't work is if you have a class derived from multiple instantiations of foo, such as
struct foo_sub2 : foo<int>, foo<double> { };
You can't do that in C++11. You would essentially have to quantify over all class types and check if any of them is a base of the candidate.
There was a proposal in TR2 (which I hear is now defunct), possibly making it into C++14, to add traits std::bases and std::direct_bases which enumerate base classes of a given class, and thus effectively solve your problem (i.e. apply your existing trait to each base class).
GCC does provide this trait in <tr2/type_traits>, if that helps.
I am on my phone, so this might not work.
The goal is to use SFINAE and overloading to ask the question "is there a base class that matches this compile time traits question?"
template<template<typename>class Test>
struct helper {
static std::false_type test(...);
template<typename T, typename=typename std::enable_if< Test<T>::value >
static std::true_type test(T const&);
};
template<template<typename>class Test, typename T, typename=void>
struct exactly_one_base_matches :std::false_type {};
template<template<typename>class Test, typename T>
struct exactly_one_base_matches<Test,T,
typename std::enable_if<decltype(helper<Test>::test(std::declval<T>()))::value>::type>
:std::true_type {};
If that does not work for a generic test, one where test does pattern matching might. ... might need to be replaced. I cannot think of a way to deal with multiple bases that pass the test...
I think we can do better. There are three possible results from calling the above.
First, one parent or self matches the test. Second, it matches the catch-all. Third, it is ambiguous because it could pass the test in more than one way.
If we improve the catch-all to catch everything at low priority (Ts...&& maybe), we can make failure to compile a success condition.
Return true from SFINAE, true from match-one, and false from catch-all match-none.