Можно ли проверить наличие шаблонов членов только по идентификатору?

Можем ли мы обнаружить элементы 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

Мне все еще грустно, что я не мог обнаружить перегруженные функции... Но это было весело:)