2016-10-01 2 views
11

Можно ли обнаружить элемент function template, variable template, class/struct/union template или alias template, не зная сумму, или характер template/non-template параметров?Можно ли проверить наличие шаблонов членов только по идентификатору?

Когда я пытаюсь думать об этом, мне ничего не приходит в голову. Но давайте структуру с шаблоном функции члена:

struct foo 
{ 
    // Really random. Let's 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 шаблон существует?

Инстанцирование на основе черты типа здесь не применимы, потому что (теоретически) мы не имеем знания , какие параметры мы должны использовать, в каком порядке и , сколько из них. Может быть, какой-то волшебный метод поиска был бы уместным? Или, может быть, это просто невозможно?


При поиске, я нашел this question, но решения в ответах требуют знаний о природе 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 
    { }; 
}; 
+0

Если есть два таких метода, я угадайте, вы также хотите, чтобы он вернул истину? –

+0

@AaronMcDaid Это было бы здорово! Но я боюсь, что добавление возможности перегрузки может еще больше осложнить ситуацию. – xinaiz

+0

Я уверен, что я видел ответ на этот вопрос всего несколько дней назад, в котором также был рассмотрен случай перегрузки. Не могу найти его сейчас. Я буду продолжать смотреть! –

ответ

5

Я могу показать вам, как определить шаблон структуры:

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 и т. д. для обнаружения шаблонов элементов.

В случае, если вы хотите, чтобы обнаружить типы vs. мета-типов, то есть шаблоны, как 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{}; 
+0

Оба решения не будут работать с такими типами, как 'template struct R {};'. – skypjack

+0

@skypjack Правильно. Однако я понятия не имею, как это можно сделать, когда вы смешиваете тип/не-тип, и вы ничего не знаете о них. – vsoftco

+0

Достаточно честный. Я не сказал, что у меня есть идея об этом, но это может быть что-то для читателя. Это все. ;-) – skypjack

4

Этот подход работает при наличии нескольких перегрузки, возвращая false_type тогда и только тогда, когда нет методов или членов, которые называются bar. Это не говорит нам ничего полезного о том, что bar (-ы) есть (хотя бы об этом позже).

(Примечание: Этот ответ (и вопрос?) Является обманом. Я узнал об этой технике на SO только несколько дней назад. Но я не могу найти оригинал!)

Это использует void_t, что вам может понадобиться, чтобы определить себя (не в C++ 11, к примеру):

template<typename ...T> 
struct voider { using type = void; }; 
template<typename ...T> 
using void_t = typename voider<T...> :: type; 

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) выдаст ошибку неоднозначности, если, и только если существует элемент bar в T. Мы можем использовать это:

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, метод шаблона с тремя параметрами шаблона шаблона, ...), то я думаю, что вы можете тест для каждого из этих шаблонов индивидуально. Но в конечном итоге существуют ограничения на то, что вы можете обнаружить с помощью конечного числа таких шаблонов. (А то, что вы имеете в виду шаблонные методы, не калиброванных структур, могли бы сделать это более трудным

+0

Каким должен быть аргумент шаблона при вызове 'has_at_least_one_bar'? Когда я пытаюсь 'just_a_bar', то« MultipleBars »дважды уходит от него. – xinaiz

+0

Я забыл добавить пример использования. Я только что добавил несколько примеров. 'just_a_bar' - это деталь реализации этой черты. Нам нужен «фиктивный» тип, который имеет (бесполезный) метод/член bar. –

+1

Почему не просто 'template using void_t = void;'? Я думаю, что потребность в промежуточной «структуре» была вызвана дефектом в стандарте, но теперь простое определение должно работать. – vsoftco

3

В C++ 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 {};).

1

Я думаю, что я получил его. Благодаря Aaron McDaid и vsoftco ответов, я удался обнаружить шаблоны типа элемента (alias template, struct template, class template и union template), member function templates с дополнительным недостатком и member variable templates.

Эта реализация имеет некоторые недостатки:

  • класса foo, что мы проверить на наличие имени bar musn't быть final типа.
  • Шаблоны с параметрами смешанного типа/не-типа/шаблона шаблона не будут обнаружены.
  • Код длинный.

Дополнительный недостаток заключается в том:

  • [Примечание: Будет исправлено в ближайшее время!] Проверка 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 
{ }; 

Live example

Пример вывода:

---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 

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

Смежные вопросы