Проверьте, начал ли тип [duplicate]

Учитывая, что никто не упомянул об этом ...

Некоторые языки высокого уровня, такие как Python и Java, поставляются с инструментами для преодоления ограничений двоичной с плавающей запятой. Например:

Ни одно из этих решений не идеально (особенно, если мы смотрим на выступления, или если мы требуют очень высокой точности), но все же они решают большое количество проблем с двоичной арифметикой с плавающей запятой.

102
задан Barry 9 May 2016 в 18:12
поделиться

10 ответов

Я не уверен, правильно ли я вас понимаю, но вы можете использовать SFINAE для обнаружения присутствия функции во время компиляции. Пример из моего кода (тесты, если класс имеет функцию-член size_t used_memory () const).

template<typename T>
struct HasUsedMemoryMethod
{
    template<typename U, size_t (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
    template<typename U> static int Test(...);
    static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, std::true_type)
{
        // We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, std::false_type)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
    ReportMemUsage(m, 
        std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
}
78
ответ дан Oktalist 26 August 2018 в 13:19
поделиться

Вот некоторые фрагменты использования: * Кишки для всего этого дальше

Проверьте член x в данном классе. Может быть var, func, class, union или enum:

CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;

Проверить функцию-член void x():

//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;

Проверить переменную-член x:

CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;

Проверить класс участника x:

CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;

Проверить членство в союзе x:

CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;

Проверить членство enum x:

CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;

Проверить любую функцию-член x независимо от сигнатуры:

CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

ИЛИ

CREATE_MEMBER_CHECKS(x);  //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;

Подробности и ядро:

/*
    - Multiple inheritance forces ambiguity of member names.
    - SFINAE is used to make aliases to member names.
    - Expression SFINAE is used in just one generic has_member that can accept
      any alias we pass it.
*/

//Variadic to force ambiguity of class members.  C++11 and up.
template <typename... Args> struct ambiguate : public Args... {};

//Non-variadic version of the line above.
//template <typename A, typename B> struct ambiguate : public A, public B {};

template<typename A, typename = void>
struct got_type : std::false_type {};

template<typename A>
struct got_type<A> : std::true_type {
    typedef A type;
};

template<typename T, T>
struct sig_check : std::true_type {};

template<typename Alias, typename AmbiguitySeed>
struct has_member {
    template<typename C> static char ((&f(decltype(&C::value))))[1];
    template<typename C> static char ((&f(...)))[2];

    //Make sure the member name is consistently spelled the same.
    static_assert(
        (sizeof(f<AmbiguitySeed>(0)) == 1)
        , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
    );

    static bool const value = sizeof(f<Alias>(0)) == 2;
};

Макросы (El Diablo!):

CREATE_MEMBER_CHECK:

//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)                                         \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct Alias_##member;                                                      \
                                                                            \
template<typename T>                                                        \
struct Alias_##member <                                                     \
    T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>  \
> { static const decltype(&T::member) value; };                             \
                                                                            \
struct AmbiguitySeed_##member { char member; };                             \
                                                                            \
template<typename T>                                                        \
struct has_member_##member {                                                \
    static const bool value                                                 \
        = has_member<                                                       \
            Alias_##member<ambiguate<T, AmbiguitySeed_##member>>            \
            , Alias_##member<AmbiguitySeed_##member>                        \
        >::value                                                            \
    ;                                                                       \
}

CREATE_MEMBER_VAR_CHECK:

//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)                                   \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_var_##var_name : std::false_type {};                      \
                                                                            \
template<typename T>                                                        \
struct has_member_var_##var_name<                                           \
    T                                                                       \
    , std::integral_constant<                                               \
        bool                                                                \
        , !std::is_member_function_pointer<decltype(&T::var_name)>::value   \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_SIG_CHECK:

//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)    \
                                                                            \
template<typename T, typename = std::true_type>                             \
struct has_member_func_##templ_postfix : std::false_type {};                \
                                                                            \
template<typename T>                                                        \
struct has_member_func_##templ_postfix<                                     \
    T, std::integral_constant<                                              \
        bool                                                                \
        , sig_check<func_sig, &T::func_name>::value                         \
    >                                                                       \
> : std::true_type {}

CREATE_MEMBER_CLASS_CHECK:

//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_class_##class_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_class_##class_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_class<                                    \
            typename got_type<typename T::class_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_UNION_CHECK:

//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)               \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_union_##union_name : std::false_type {};  \
                                                            \
template<typename T>                                        \
struct has_member_union_##union_name<                       \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_union<                                    \
            typename got_type<typename T::union_name>::type \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_ENUM_CHECK:

//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)                 \
                                                            \
template<typename T, typename = std::true_type>             \
struct has_member_enum_##enum_name : std::false_type {};    \
                                                            \
template<typename T>                                        \
struct has_member_enum_##enum_name<                         \
    T                                                       \
    , std::integral_constant<                               \
        bool                                                \
        , std::is_enum<                                     \
            typename got_type<typename T::enum_name>::type  \
        >::value                                            \
    >                                                       \
> : std::true_type {}

CREATE_MEMBER_FUNC_CHECK:

//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)          \
template<typename T>                            \
struct has_member_func_##func {                 \
    static const bool value                     \
        = has_member_##func<T>::value           \
        && !has_member_var_##func<T>::value     \
        && !has_member_class_##func<T>::value   \
        && !has_member_union_##func<T>::value   \
        && !has_member_enum_##func<T>::value    \
    ;                                           \
}

CREATE_MEMBER_CHECKS:

//Create all the checks for one member.  Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)    \
CREATE_MEMBER_CHECK(member);            \
CREATE_MEMBER_VAR_CHECK(member);        \
CREATE_MEMBER_CLASS_CHECK(member);      \
CREATE_MEMBER_UNION_CHECK(member);      \
CREATE_MEMBER_ENUM_CHECK(member);       \
CREATE_MEMBER_FUNC_CHECK(member)
9
ответ дан Brett Rossier 26 August 2018 в 13:19
поделиться

Хорошо. Вторая попытка. Это нормально, если вам это не нравится, я ищу больше идей.

Статья Херба Саттера рассказывает о чертах. Таким образом, у вас может быть класс признаков, у экземпляра по умолчанию есть резервное поведение, а для каждого класса, где существует ваша функция-член, класс признаков специализирован для вызова функции-члена. Я полагаю, что в статье Херба упоминается техника для этого, так что она не требует большого количества копирования и вставки.

Как я уже сказал, возможно, вам не нужна дополнительная работа с «тегами», классы, которые реализуют этот элемент. В этом случае я смотрю на третье решение ....

2
ответ дан Chris Jester-Young 26 August 2018 в 13:19
поделиться

Принятый ответ на этот вопрос интроспекции член-функции compiletime, хотя он по праву популярен, имеет зацепку, которая может наблюдаться в следующей программе:

#include <type_traits>
#include <iostream>
#include <memory>

/*  Here we apply the accepted answer's technique to probe for the
    the existence of `E T::operator*() const`
*/
template<typename T, typename E>
struct has_const_reference_op
{
    template<typename U, E (U::*)() const> struct SFINAE {};
    template<typename U> static char Test(SFINAE<U, &U::operator*>*);
    template<typename U> static int Test(...);
    static const bool value = sizeof(Test<T>(0)) == sizeof(char);
};

using namespace std;

/* Here we test the `std::` smart pointer templates, including the
    deprecated `auto_ptr<T>`, to determine in each case whether
    T = (the template instantiated for `int`) provides 
    `int & T::operator*() const` - which all of them in fact do.
*/ 
int main(void)
{
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value << endl;
    return 0;
}

Построено с GCC 4.6.3 , программные выходы 110 - информируя нас о том, что T = std::shared_ptr<int> предоставляет not , предоставить int & T::operator*() const.

Если вы еще не разумны в этом вопросе, то посмотрите на определение std::shared_ptr<T> в заголовке <memory> пролить свет. В этой реализации std::shared_ptr<T> происходит из базового класса, из которого он наследует operator*() const. Таким образом, экземпляр шаблона SFINAE<U, &U::operator*>, который представляет собой «поиск» оператора для U = std::shared_ptr<T>, не будет выполняться, поскольку std::shared_ptr<T> не имеет operator*() в своем собственном праве, а создание экземпляра шаблона не «наследует».

Эта защелка не влияет на хорошо известный подход SFINAE, используя «Theofofofof () Trick», для обнаружения только того, имеет ли T некоторая функция-член mf (см., например, этот ответ и Комментарии). Но установление того, что T::mf существует, часто (обычно?) Не достаточно хорошо: вам также может потребоваться установить, что у него есть желаемая подпись. Вот где проиллюстрирована техническая оценка. Указанный вариант желаемой сигнатуры вписывается в параметр типа шаблона, который должен удовлетворять &T::mf для успешного использования зонда SFINAE. Но этот метод экземпляра шаблона дает неправильный ответ, когда T::mf унаследован.

Безопасный метод SFINAE для интроспекции компиляции T::mf должен избегать использования &T::mf в аргументе шаблона для создания экземпляра типа от которого зависит разрешение шаблона функции SFINAE. Вместо этого разрешение функции шаблона SFINAE может зависеть только от точно соответствующих деклараций типа, используемых в качестве типов аргументов перегруженной функции зондирования SFINAE.

. В ответ на вопрос, который соблюдает это ограничение, я проиллюстрирую обнаружение компиляции E T::operator*() const для произвольных T и E. Тот же шаблон применит mutatis mutandis для проверки любой другой сигнатуры метода участника.

#include <type_traits>

/*! The template `has_const_reference_op<T,E>` exports a
    boolean constant `value that is true iff `T` provides
    `E T::operator*() const`
*/ 
template< typename T, typename E>
struct has_const_reference_op
{
    /* SFINAE operator-has-correct-sig :) */
    template<typename A>
    static std::true_type test(E (A::*)() const) {
        return std::true_type();
    }

    /* SFINAE operator-exists :) */
    template <typename A> 
    static decltype(test(&A::operator*)) 
    test(decltype(&A::operator*),void *) {
        /* Operator exists. What about sig? */
        typedef decltype(test(&A::operator*)) return_type; 
        return return_type();
    }

    /* SFINAE game over :( */
    template<typename A>
    static std::false_type test(...) {
        return std::false_type(); 
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<T>(0,0)) type;

    static const bool value = type::value; /* Which is it? */
};

В этом решении перегруженная функция зонда SFINAE test() «вызывается рекурсивно», , (Конечно, он вообще не вызывается вообще, он просто имеет возвращаемые типы гипотетических вызовов, разрешенных компилятором.)

Нам нужно исследовать хотя бы одну и не более двух точек информации:

  • Существует ли вообще T::operator*()? Если нет, мы закончили.
  • Учитывая, что существует T::operator*(), является ли его подпись E T::operator*() const?

Мы получаем ответы, оценивая возвращаемый тип один вызов test(0,0). Это делается с помощью:

    typedef decltype(test<T>(0,0)) type;

Этот вызов может быть разрешен в /* SFINAE operator-exists :) */ перегрузке test(), или он может решить перегрузку /* SFINAE game over :( */. Он не может решить перегрузку /* SFINAE operator-has-correct-sig :) */, потому что тот ожидает только один аргумент, и мы проходим два.

Почему мы проходим два? Просто заставить разрешение исключить /* SFINAE operator-has-correct-sig :) */. Второй аргумент не имеет никакой другой значимости.

Этот вызов test(0,0) разрешит /* SFINAE operator-exists :) */, если первый аргумент 0 удовлетворяет первому типу параметра этой перегрузки, который является decltype(&A::operator*), с A = T. 0 будет удовлетворять этому типу на всякий случай T::operator*.

Предположим, что компилятор говорит «Да». Затем это происходит с /* SFINAE operator-exists :) */, и ему нужно определить тип возврата вызова функции, который в этом случае decltype(test(&A::operator*)) - тип возврата еще одного вызова test().

На этот раз , мы передаем только один аргумент, &A::operator*, который мы теперь знаем, или мы не были бы здесь. Вызов test(&A::operator*) может разрешить либо /* SFINAE operator-has-correct-sig :) */, либо снова, чтобы разрешить /* SFINAE game over :( */. Вызов будет соответствовать /* SFINAE operator-has-correct-sig :) */ на всякий случай, если &A::operator* удовлетворяет типу одного параметра этой перегрузки, который E (A::*)() const, с A = T.

Компилятор скажет «Да», если T::operator* имеет эту желаемую подпись, а затем снова должен оценивать возвращаемый тип перегрузки. Больше нет «рекурсий»: это std::true_type.

Если компилятор не выбирает /* SFINAE operator-exists :) */ для вызова test(0,0) или не выбирает /* SFINAE operator-has-correct-sig :) */ для вызова test(&A::operator*), тогда в любом случае он идет с /* SFINAE game over :( */, а конечным типом возврата является std::false_type.

Вот тестовая программа, в которой показан шаблон, который дает ожидаемые ответы в разнообразной выборке случаев (GCC 4.6.3 снова ).

// To test
struct empty{};

// To test 
struct int_ref
{
    int & operator*() const {
        return *_pint;
    }
    int & foo() const {
        return *_pint;
    }
    int * _pint;
};

// To test 
struct sub_int_ref : int_ref{};

// To test 
template<typename E>
struct ee_ref
{
    E & operator*() {
        return *_pe;
    }
    E & foo() const {
        return *_pe;
    }
    E * _pe;
};

// To test 
struct sub_ee_ref : ee_ref<char>{};

using namespace std;

#include <iostream>
#include <memory>
#include <vector>

int main(void)
{
    cout << "Expect Yes" << endl;
    cout << has_const_reference_op<auto_ptr<int>,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int &>::value;
    cout << has_const_reference_op<shared_ptr<int>,int &>::value;
    cout << has_const_reference_op<std::vector<int>::iterator,int &>::value;
    cout << has_const_reference_op<std::vector<int>::const_iterator,
            int const &>::value;
    cout << has_const_reference_op<int_ref,int &>::value;
    cout << has_const_reference_op<sub_int_ref,int &>::value  << endl;
    cout << "Expect No" << endl;
    cout << has_const_reference_op<int *,int &>::value;
    cout << has_const_reference_op<unique_ptr<int>,char &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int const &>::value;
    cout << has_const_reference_op<unique_ptr<int>,int>::value;
    cout << has_const_reference_op<unique_ptr<long>,int &>::value;
    cout << has_const_reference_op<int,int>::value;
    cout << has_const_reference_op<std::vector<int>,int &>::value;
    cout << has_const_reference_op<ee_ref<int>,int &>::value;
    cout << has_const_reference_op<sub_ee_ref,int &>::value;
    cout << has_const_reference_op<empty,int &>::value  << endl;
    return 0;
}

Есть ли новые недостатки в этой идее? Может ли он стать более общим без лишних падений, чтобы избежать этого?

33
ответ дан Community 26 August 2018 в 13:19
поделиться

Этого должно быть достаточно, если вы знаете имя функции-члена, которую вы ожидаете. (В этом случае функция bla не может создать экземпляр, если нет какой-либо функции-члена (писать все, что работает в любом случае, является жестким, потому что отсутствует частичная специализация функции. Возможно, вам придется использовать шаблоны классов). Кроме того, разрешающая структура (которая аналогичен enable_if) также может быть запрограммирован на тип функции, которую вы хотите иметь в качестве члена.

template <typename T, int (T::*) ()> struct enable { typedef T type; };
template <typename T> typename enable<T, &T::i>::type bla (T&);
struct A { void i(); };
struct B { int i(); };
int main()
{
  A a;
  B b;
  bla(b);
  bla(a);
}
11
ответ дан Head Geek 26 August 2018 в 13:19
поделиться

Без поддержки C ++ 11 (decltype) это может работать:

SSCCE

#include <iostream>
using namespace std;

struct A { void foo(void); };
struct Aa: public A { };
struct B { };

struct retA { int foo(void); };
struct argA { void foo(double); };
struct constA { void foo(void) const; };
struct varA { int foo; };

template<typename T>
struct FooFinder {
    typedef char true_type[1];
    typedef char false_type[2];

    template<int>
    struct TypeSink;

    template<class U>
    static true_type &match(U);

    template<class U>
    static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);

    template<class U>
    static false_type &test(...);

    enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
};

int main() {
    cout << FooFinder<A>::value << endl;
    cout << FooFinder<Aa>::value << endl;
    cout << FooFinder<B>::value << endl;

    cout << FooFinder<retA>::value << endl;
    cout << FooFinder<argA>::value << endl;
    cout << FooFinder<constA>::value << endl;
    cout << FooFinder<varA>::value << endl;
}

Как он, надеюсь, работает

A , Aa и B являются рассматриваемыми кланами, Aa является специальным, который наследует элемент, который мы ищем.

В FooFinder true_type и false_type ] - это замены для соответствующих классов C ++ 11. Также для понимания мета-программирования шаблонов они раскрывают саму основу трюка SFINAE-sizeof.

TypeSink - это структура шаблона, которая используется позже, чтобы потопить интегральный результат sizeof в экземпляр шаблона для формирования типа.

Функция match - это еще один шаблон типа SFINAE, который остается без общего экземпляра. Поэтому он может быть создан только в том случае, если тип его аргумента совпадает с типом, для которого он был специализирован.

Обе функции test вместе с объявлением перечисления окончательно образуют центральный шаблон SFINAE. Существует универсальный метод, использующий эллипсис, который возвращает false_type и аналог с более конкретными аргументами.

Чтобы иметь возможность создать экземпляр функции test с аргументом шаблона T , функция match должна быть инстанцирована, так как ее тип возврата требуется для создания экземпляра аргумента TypeSink. Предостережение заключается в том, что &U::foo, будучи завернутым в аргумент функции, является not , на который ссылается внутри специализации аргумента шаблона, поэтому унаследованный поиск элементов все еще имеет место.

1
ответ дан Kamajii 26 August 2018 в 13:19
поделиться

Для этого нам нужно будет использовать:

  1. Перегрузка шаблона функции с разными типами возврата в зависимости от того, доступен ли этот метод
  2. В соответствии с мета-условными обозначениями в заголовке type_traits мы хотим вернуть true_type или false_type из наших перегрузок
  3. Объявите перегрузку true_type, ожидающую перегрузки int и false_type, ожидающие использования Variadic Parameters: «Самый низкий приоритет преобразования многоточия при разрешении перегрузки»
  4. При определении спецификации шаблона для функции true_type мы будем использовать declval и decltype , позволяющие нам обнаружить функцию, не зависящую от различий или перегрузок типа возврата между методами

Вы можете увидеть живой пример этого здесь , но я объясню это ниже:

Я хочу проверить существование функции с именем test, которая принимает тип, конвертируемый из int, тогда мне нужно было бы объявить эти две функции:

template <typename T, typename S = decltype(declval<T>().test(declval<int>))> static true_type hasTest(int);
template <typename T> static false_type hasTest(...);
  • decltype(hasTest<a>(0))::value is true (Примечание: нет необходимости создавать специальные функции для работы с перегрузкой void a::test(), принимается void a::test(int))
  • decltype(hasTest<b>(0))::value есть true (Поскольку int можно преобразовать в double, принимается int b::test(double) независимо от типа возврата)
  • decltype(hasTest<c>(0))::value is false (c не имеет метода с именем test который принимает тип, конвертируемый из int, поэтому это не принимается)

Это решение имеет 2 недостатка:

  1. Требуется объявление каждой пары пары функций
  2. Создает загрязнение пространства имен, особенно если мы хотим проверить похожие имена, например, что бы мы назвали функцией, которая хотела проверить метод test()?

Поэтому важно, чтобы эти функции были объявлены в пространстве имен деталей, или, в идеале, если они должны использоваться только с классом, они должны быть объявлены частным образом этим классом. С этой целью я написал макрос, который поможет вам абстрагировать эту информацию:

#define FOO(FUNCTION, DEFINE) template <typename T, typename S = decltype(declval<T>().FUNCTION)> static true_type __ ## DEFINE(int); \
                              template <typename T> static false_type __ ## DEFINE(...); \
                              template <typename T> using DEFINE = decltype(__ ## DEFINE<T>(0));

Вы можете использовать это как:

namespace details {
    FOO(test(declval<int>()), test_int)
    FOO(test(), test_void)
}

Затем вызывать details::test_int<a>::value или details::test_void<a>::value даст true или false для встроенного кода или метапрограммирования.

3
ответ дан O'Neil 26 August 2018 в 13:19
поделиться

Я считаю, что ответ, который вы ищете, здесь.

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

и немного более заполненный пример здесь

http://pastie.org/298994

Я использую эту технику для обнаружения наличие поддерживающего операционного оператора & lt; на класс, о котором идет речь, а затем генерировать другой бит кода в зависимости.

Я не думал, что это возможно, прежде чем найти связанное решение, но это очень аккуратный трюк. Проведите время, понимая код, и это очень стоит.

Brad

1
ответ дан user 26 August 2018 в 13:19
поделиться

Вот более простой ответ на ответ Майка Кингхана. Это обнаружит унаследованные методы. Он также проверит наличие точной подписи (в отличие от подхода jrok, который позволяет преобразовывать аргументы).

template <class C>
class HasGreetMethod
{
    template <class T>
    static std::true_type testSignature(void (T::*)(const char*) const);

    template <class T>
    static decltype(testSignature(&T::greet)) test(std::nullptr_t);

    template <class T>
    static std::false_type test(...);

public:
    using type = decltype(test<C>(nullptr));
    static const bool value = type::value;
};

struct A { void greet(const char* name) const; };
struct Derived : A { };
static_assert(HasGreetMethod<Derived>::value, "");

Пример выполнения

3
ответ дан Valentin Milea 26 August 2018 в 13:19
поделиться

Вы можете использовать std :: is_member_function_pointer

class A {
   public:
     void foo() {};
}

 bool test = std::is_member_function_pointer<decltype(&A::foo)>::value;
4
ответ дан Yochai Timmer 26 August 2018 в 13:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: