Можно ли проверить наличие шаблонов членов только по идентификатору?
Можем ли мы обнаружить элементы function template
, variable template
, class
/struct
/union template
или alias template
без знания количества или характера параметров template
/non-template
?
Когда я пытаюсь думать об этом, мне ничего не приходит в голову. Но пусть структура с шаблоном функции члена:
struct foo
{
// Really random. Let assume we don't know this declaration, just the name "bar"
template <class T, std::size_t N, class... Args>
void bar(T a, T b, T(&c)[N], Args const& ...);
};
Как проверить, существует ли шаблон foo::bar
?
Символы типа основанные на экземпляре не применяются здесь, потому что (теоретически) мы не знаем, какие параметры мы должны использовать, в каком порядке и сколько из них. Может быть, какой-то волшебный метод поиска был бы уместным? Или, может быть, это просто невозможно?
При поиске я нашел этот вопрос, но решения в ответах требуют знания о природе template
.
Вот моя первая неудачная попытка для обнаружения struct template
:
struct foo
{
template<class T>
struct bar { };
};
template <class T, class = void>
struct has_template_bar : std::false_type
{ };
template <class T>
struct has_template_bar <T, void> : std::true_type
{
template<template<class...> class tplt_tplt = T::bar> // Invalid default argument
struct placebo
{ };
};
Ответы
Ответ 1
Я могу показать вам, как определить шаблон структуры:
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 {};
Затем вы можете поиграть с declval
, void_t
и т.д., чтобы обнаружить шаблоны участников.
Если вы хотите определить типы и мета-типы, то есть шаблоны, например std::vector
, а не std::vector<int>
, вы можете сделать следующее:
#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
Обратите внимание, что решения не будут работать, если мета-тип имеет какие-либо непиковые параметры, например
template<class, int> struct X{};
Ответ 2
Этот подход работает при наличии нескольких перегрузок, возвращая false_type
тогда и только тогда, когда нет методов или членов, называемых bar
. Это не говорит нам ничего полезного о том, что bar
(s) есть/хотя (подробнее об этом позже).
(Примечание: Этот ответ (и вопрос?) является обманом. Я узнал об этом методе на SO только несколько дней назад, но я не могу найти оригинал!)
В этом случае используется void_t
, который вам может понадобиться определить самостоятельно (например, не в С++ 11):
template<typename ...T>
struct voider { using type = void; };
template<typename ...T>
using void_t = typename voider<T...> :: type;
bar
- это тот член, которого нас интересует, поэтому мы создаем действительно скучную структуру с элементом, называемым bar:
struct just_a_bar { int bar; };
Затем шаблон, заданный T
, который объявляет структуру, которая наследуется как от T
, так и от just_a_bar
.
template<typename T>
struct MultipleBars : public T , public just_a_bar { };
Теперь decltype(MultipleBars<T>::bar)
даст ошибку двусмысленности, если и только если в T есть член bar
. Мы можем использовать это:
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 {
};
Затем, чтобы использовать приведенное выше для реального:
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
}
Как только вы знаете bar
, вы, вероятно, захотите получить больше деталей. Если у вас есть несколько конкретных шаблонов (член без шаблона, метод шаблона только с параметрами типа, метод шаблона с двумя параметрами int
непигового типа, метод шаблона с тремя параметрами шаблона шаблона,...), то я думаю вы можете проверить каждый из этих шаблонов индивидуально. Но в конечном итоге существуют ограничения на то, что вы можете обнаружить с помощью конечного числа таких шаблонов. (И тот факт, что вы имеете в виду шаблонные методы, а не шаблонные структуры, может сделать это более сложным
Ответ 3
В С++ 14 вы можете использовать переменные шаблона, чтобы определить, является ли тип специализацией:
#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>>, "!");
}
Обратите внимание, что это не сработает, если задействованы непиковые параметры (в качестве примера template<int> struct R {};
).
Ответ 4
Я думаю, что понял. Благодаря ответам Aaron McDaid и vsoftco мне удалось обнаружить шаблоны типов участников (alias template
, struct template
, class template
и union template
), member function templates
с одним дополнительным недостатком и member variable templates
.
Эта реализация имеет некоторые недостатки:
- Класс
foo
, который мы проверяем на существование имени bar
musn't be final
.
- Шаблоны со смешанными параметрами типа type/non-type/template-template не будут обнаружены.
- Код длинный.
Дополнительный недостаток:
- [Примечание: скоро будет исправлено!] Проверка на
member function tempalates
вернет true
, если класс foo
имеет любую перегруженную функцию bar
. У меня просто не было средств для обнаружения перегруженной функции. Это также повлияет на конечную черту типа has_member_template
.
Вот реализация:
#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
{ };
Пример в реальном времени
Пример вывода:
---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
Мне все еще грустно, что я не мог обнаружить перегруженные функции... Но это было весело:)