Can we detect member function template
, variable template
, class
/struct
/union template
or alias templ
This approach works in the presence of multiple overloads, returning false_type
if and only if there are no methods, or members, called bar
. It doesn't tell us anything useful about what the bar
(s) is/are though (more on that later).
(Note: This answer (and question?) are dupes. I learned about this technique on SO only a few days ago. But I can't find the original!)
This uses void_t
, which you may need to define yourself (not in c++11, for example):
template<typename ...T>
struct voider { using type = void; };
template<typename ...T>
using void_t = typename voider<T...> :: type;
bar
is the member we are interested in, so we make a really boring struct with a member called bar:
struct just_a_bar { int bar; };
Then a template, given T
, which declares a struct that inherits from both T
and just_a_bar
.
template<typename T>
struct MultipleBars : public T , public just_a_bar { };
Now, decltype(MultipleBars<T>::bar)
will give an ambiguity error if, and only if, there is a member bar
in T. We can use this:
template<typename T, typename =void>
struct has_at_least_one_bar : public true_type {};
template<typename T>
struct has_at_least_one_bar<T, void_t< decltype(MultipleBars<T>::bar) >>
: public false_type {
};
Then, to use the above for real:
struct zero { };
struct one {
void bar(int,int);
};
struct two {
//template<typename P, typename Q> // works fine with templates too
void bar(int);
void bar(int,int);
};
int main() {
cout << boolalpha;
cout << has_at_least_one_bar<zero>{} << endl; // false
cout << has_at_least_one_bar<one>{} << endl; // true
cout << has_at_least_one_bar<two>{} << endl; // true
}
Once you know bar
exists, you probably want more detail. If you have a few specific patterns in mind (non-template member, template method with type parameters only, template method with two int
non-type parameters, template method with three template-template parameters, ...) then I think you can test for each of those patterns individually. But ultimately there are limits to what you can detect with a finite number of such patterns. (And the fact that you mean templated methods, not templated structs, might make this more difficult
I can show you how to detect a struct template:
template < class > struct check_template : std::false_type {};
// Specialize for template classes
template <template<class...> class X, class... Args>
struct check_template< X<Args...> > : std::true_type {};
You can then probably play around with declval
, void_t
, etc. to detect member templates.
In case you want to detect types vs. meta-types, i.e. templates like std::vector
and not std::vector<int>
, you can do the following:
#include <iostream>
template <template<class...> class>
constexpr bool is_template()
{
return true;
}
template <class>
constexpr bool is_template()
{
return false;
}
struct Foo{};
template<class>
struct TemplateFoo{};
int main()
{
std::cout << std::boolalpha;
std::cout << is_template<Foo>() << std::endl;
std::cout << is_template<TemplateFoo>() << std::endl;
}
Live on Coliru
Note that the solutions won't work if the meta-type has any non-type parameters, like
template<class, int> struct X{};
I think I got it. Thanks to Aaron McDaid and vsoftco answers, I succeeded in detecting member type templates (alias template
, struct template
, class template
and union template
), member function templates
with one additional drawback, and member variable templates
.
This implementation has some drawbacks:
foo
that we check for existence of name bar
musn't be final
type.Additional drawback is:
member function tempalates
will return true
, if class foo
has any overloaded function bar
. I just didn't have any means to detect overloaded function alone. This will also affect final has_member_template
type trait.Here is the implementation:
#include <iostream>
#include <type_traits>
#include <iomanip>
/***Check if type is template***/
template <template<class...> class>
constexpr bool is_template_type()
{
return true;
}
template <class>
constexpr bool is_template_type()
{
return false;
}
/***Check if T has static member function "bar" ***/
template <class, class = void>
struct has_static_member_function_bar : std::false_type
{ };
template <class T>
struct has_static_member_function_bar<T,
std::enable_if_t<std::is_function<typename std::remove_pointer<decltype(&T::bar)>::type>::value
>
> : std::true_type
{ };
/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_function_bar : std::false_type
{ };
template <class T>
struct has_member_function_bar<T,
std::enable_if_t<std::is_member_function_pointer<decltype(&T::bar)>::value
>
> : std::true_type
{ };
/***Check if T has member reference "bar" ***/
template <class, class = void>
struct has_member_reference_bar : std::false_type
{ };
template <class T>
struct has_member_reference_bar<T,
std::enable_if_t<std::is_reference<decltype(T::bar)>::value
>
> : std::true_type
{ };
/***Check if T has static member object "bar" ***/
template <class, class = void>
struct has_static_member_object_bar : std::false_type
{ };
template <class T>
struct has_static_member_object_bar<T,
std::enable_if_t<std::is_object<typename std::remove_pointer<decltype(&T::bar)>::type>::value &&
(!std::is_member_object_pointer<decltype(&T::bar)>::value)
>
> : std::true_type
{ };
/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_object_bar : std::false_type
{ };
template <class T>
struct has_member_object_bar<T,
std::enable_if_t<std::is_member_object_pointer<decltype(&T::bar)>::value
>
> : std::true_type
{ };
/***Check if T has member alias, struct, class, union template "bar" ***/
template <class, class = void>
struct has_member_type_template_bar : std::false_type
{ };
template <class T>
struct has_member_type_template_bar<T,
std::enable_if_t<is_template_type<T::template bar>()
>
> : std::true_type
{ };
/***Check if T has at least one name "bar" ***/
struct has_at_least_one_bar_impl { int bar; };
template<typename T>
struct bar_overloads : T , has_at_least_one_bar_impl { };
template<typename T, typename = void>
struct has_at_least_one_bar : std::true_type { };
template<typename T>
struct has_at_least_one_bar<T, std::void_t< decltype(bar_overloads<T>::bar) >>
: std::false_type { };
/***Check if T has member object, reference, not-overloaded function "bar" ***/
template <class, class = void>
struct has_non_type_non_overloaded_member_bar : std::false_type
{ };
template <class T>
struct has_non_type_non_overloaded_member_bar<T,
std::void_t<decltype((void)(T::bar))>> : std::true_type
{ };
/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_type_member_bar : std::false_type
{ };
template <class T>
struct has_type_member_bar<T,
std::void_t<typename T::bar>> : std::true_type
{ };
/***Check if T has no more than one member "bar" ***/
template<class, class = void, class = void>
struct has_at_most_one_bar : std::false_type
{ };
template<class T>
struct has_at_most_one_bar<T,
std::enable_if_t<
has_type_member_bar<T>::value ||
has_non_type_non_overloaded_member_bar<T>::value
>
> : std::true_type
{ };
/***Check if T has member function template "bar" ***/
template <class, class = void>
struct has_member_function_template_bar : std::false_type
{ };
template <class T>
struct has_member_function_template_bar<T,
std::enable_if_t<has_at_least_one_bar<T>::value &&
(!has_member_type_template_bar<T>::value) &&
(!has_non_type_non_overloaded_member_bar<T>::value) &&
(!has_member_function_bar<T>::value) &&
(!has_type_member_bar<T>::value)
>
> : std::true_type
{ };
/***Check if T has member variable template "bar" ***/
template <class, class = void>
struct has_member_variable_template_bar : std::false_type
{ };
template <class T>
struct has_member_variable_template_bar<T,
std::enable_if_t<has_at_least_one_bar<T>::value &&
(!has_member_type_template_bar<T>::value) &&
(!has_member_function_template_bar<T>::value) &&
(!has_type_member_bar<T>::value) &&
(!has_static_member_function_bar<T>::value) &&
(!has_member_function_bar<T>::value) &&
(!has_member_object_bar<T>::value) &&
(!has_member_reference_bar<T>::value) &&
(!has_static_member_object_bar<T>::value)>
> : std::true_type
{ };
/***Check if T has any member template "bar" ***/
template <class, class = void>
struct has_member_template_bar : std::false_type
{ };
template <class T>
struct has_member_template_bar<T,
std::enable_if_t<has_member_type_template_bar<T>::value ||
has_member_function_template_bar<T>::value ||
has_member_variable_template_bar<T>::value>
> : std::true_type
{ };
Live example
Example output:
---Has type template bar---
consists_no_bar: false
consists_alias: false
consists_struct: false
consists_class: false
consists_union: false
consists_variable: false
consists_function: false
consists_overloaded_func: false
consists_reference: false
consists_t_alias: true
consists_t_struct: true
consists_t_class: true
consists_t_union: true
consists_t_variable: false
consists_t_function: false
consists_t_overloaded_function: false
consists_s_variable: false
consists_s_function: false
consists_s_overloaded_func: false
consists_s_t_function: false
consists_s_t_overloaded_function: false
--Has member function template bar---
consists_no_bar: false
consists_alias: false
consists_struct: false
consists_class: false
consists_union: false
consists_variable: false
consists_function: false
consists_overloaded_func: true // implmementation bug
consists_reference: false
consists_t_alias: false
consists_t_struct: false
consists_t_class: false
consists_t_union: false
consists_t_variable: false
consists_t_function: true
consists_t_overloaded_function: true
consists_s_variable: false
consists_s_function: false
consists_s_overloaded_func: true // implmementation bug
consists_s_t_function: true
consists_s_t_overloaded_function: true
--Has member variable template bar---
consists_no_bar: false
consists_alias: false
consists_struct: false
consists_class: false
consists_union: false
consists_variable: false
consists_function: false
consists_overloaded_func: false
consists_reference: false
consists_t_alias: false
consists_t_struct: false
consists_t_class: false
consists_t_union: false
consists_t_variable: true
consists_t_function: false
consists_t_overloaded_function: false
consists_s_variable: false
consists_s_function: false
consists_s_overloaded_func: false
consists_s_t_function: false
consists_s_t_overloaded_function: false
--Has any member template bar---
consists_no_bar: false
consists_alias: false
consists_struct: false
consists_class: false
consists_union: false
consists_variable: false
consists_function: false
consists_overloaded_func: true // implmementation bug
consists_reference: false
consists_t_alias: true
consists_t_struct: true
consists_t_class: true
consists_t_union: true
consists_t_variable: true
consists_t_function: true
consists_t_overloaded_function: true
consists_s_variable: false
consists_s_function: false
consists_s_overloaded_func: true // implmementation bug
consists_s_t_function: true
consists_s_t_overloaded_function: true
I'm still sad that I couldn't detect overloaded functions... But it was fun :)
In C++14, you can use template variables to detect if a type is a specialization:
#include <type_traits>
template<typename>
constexpr bool is_spec = false;
template<template<typename...> class T, typename... U>
constexpr bool is_spec<T<U...>> = true;
struct S {};
template<typename> struct R {};
int main() {
static_assert(not is_spec<S>, "!");
static_assert(is_spec<R<void>>, "!");
}
Note that it won't work if non-type parameters are involved (as an example template<int> struct R {};
).