55

Вопрос по visual-studio, c++ – Как определить, есть ли конкретная переменная-член в классе?

Для создания шаблонной функции алгоритма мне нужно знать, есть ли x или X (и y или Y) в классе, который является аргументом шаблона. Это может быть полезно при использовании моей функции для класса MFC CPoint, класса GDI + PointF или некоторых других. Все они используют разные х в них. Мое решение может быть сведено к следующему коду:


template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::type b = 0) { return false; }

struct P1 {int x; };
struct P2 {float X; };
// it also could be struct P3 {unknown_type X; };

int main()
{
    P1 p1 = {1};
    P2 p2 = {1};

    Check_x(p1); // must return true
    Check_x(p2); // must return false

    return 0;
}

Но он не компилируется в Visual Studio, а компилируется в GNU C ++. С Visual Studio я мог бы использовать следующий шаблон:


template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }

Но он не компилируется в GNU C ++. Есть ли универсальное решение?

UPD: Структуры P1 и P2 здесь только для примера. Могут быть любые классы с неизвестными участниками.

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

  • @DarioP: я добавил обновление с объяснением. Как видите, объяснение довольно длинное, потому что правильное количество деталей всегда зависит от знаний читателя. На благо начинающих я предполагал очень мало. Надеюсь, это понятно, но не скучно.

    от
  • Ваш ответ неприменим к моему вопросу, потому что я не знаю тип элемента x (или X). Это может быть int или float или что-то еще (включая пользовательские типы с определенными операциями + - * /).

    от Kirill V. Lyadvinsky
  • Вы правы. has_xxx уже находится в Boost и отвечает на вопрос. Я не согласен со второй ссылкой, поскольку проверка архетипов и концепций - это две стороны одной медали.

    от
  • Тогда я не вижу способа узнать его с помощью шаблонов (может быть, я ошибаюсь). Если типы данных x были разными в P1 & amp; Тогда P2 может быть, мы могли бы использовать sizeof, чтобы вернуть true или false.

    от
  • Концептуальные черты больше не поддерживаются, потому что в то время как эта функция превратилась в эти две:boost.org/doc/libs/1_42_0/libs/mpl/doc/refmanual/… другие функции вошли в Concepts_checks, например:boost.org/doc/libs/1_42_0/libs/concept_check/…

    от
  • @Jurak Blaho: проблема в GCC 4.7.2. Он работает с Clang 3.2 и IIRC, а также с GCC 4.8. (Это позор, чтоliveworkspace некоторое время не работал.)

    от
  • Спасибо, я бы проголосовал во второй раз, если бы мог! Это великолепно!

    от
  • Хорошее решение - вы столкнетесь с проблемами, если у X есть личная переменная-член id.

    от
  • Не могли бы вы объяснить немного больше, как это работает, пожалуйста?

    от
  • Это не похоже на работу:ideone.com/QDhC8A

    от
  • Я не думаю, что вам нужноdeclval вообще, в моем тестированииdecltype(T::id, void()) работает отлично

    от
  • Сначала я не понял идею. Это именно то, что мне было нужно. Это решение работает как в MSVC ++ 2008, так и в g ++ 4.2.4.

    от Kirill V. Lyadvinsky
  • Может быть, важно отметить, что это не работает для унаследованных методов ... но, возможно, это и было задумано. Я думаю, что std :: integra_constant является здесь ограничением.

    от
  • Потому что на самом деле я не знаю, что структура P2 содержит большой X, а P1 содержит маленький x. Эти структуры здесь только для примера. Там могут быть любые учебные заведения или классы.

    от Kirill V. Lyadvinsky
  • В моем вопросе есть способ (на самом деле есть два разных способа). Но я не знаю, как распознать его в обоих компиляторах.

    от Kirill V. Lyadvinsky
  • Шаблоны Variadic недопустимы в текущем стандарте C ++.

    от Kirill V. Lyadvinsky
  • Существует более двух типов. Посмотрите на мой комментарий к ответу Навина.

    от Kirill V. Lyadvinsky
  • @rmn, это делаетnot проверить наinteger член, однако, он проверяет любые данные или функции, вызываемыеxс произвольным типом. Единственная цель введения имени члена состоит в том, чтобы иметь возможную двусмысленность для поиска имени члена -type члена не имеет значения.

    от
  • теперь опубликовано полное объяснение этого кода .. по адресу:cpptalk.wordpress.com/2009/09/12/…  litb, вы можете проверить, правильно ли я понял :)

    от
  • да, задержи дыханиеgroups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/… (связанный с SO потоком, который Вы связываете также одним парнем). Не беспокойтесь, если вы сразу не получите то, что делает код этого парня. Это очень умно и заняло у меня тоже много времени

    от
  • Я добавил пост в своем блоге об этом, надеюсь, вы, ребята, не против :) наcpptalk.wordpress.com/2009/09/11/… Интересно читать.

    от
  • Я написал подробное объяснение правильного кода для решения этой проблемы, и он доступен здесь:cpptalk.wordpress.com/2009/09/12/… , Извините, что сделал этот комментарий дважды, но я чувствую, что он относится к основному посту.

    от rmn
  • @litb: посмотрите на ссылку в конце моего ответа - я думаю, что это объясняет проблему (и почему компиляторы отклоняют ее, и действительно ли это разрешено стандартом C ++ 98).

    от James Hopkin
  • +1: интересный вызов :-)

    от James Hopkin
  • возможный дубликатIs it possible to write a C++ template to check for a function's existence?

    от Mat
  • Я не верю, что второй способ является стандартным (целочисленные константные выражения могут не использовать op == с операндами, включающими op & amp;). Но первый способ выглядит правильно. Что msvc ++ говорит об этом?

    от Johannes Schaub - litb
10 ответов
  • 46

    Другой способ это тот, который опирается на

    SFINAE для выражений тоже. Если поиск имени приводит к неоднозначности, компилятор отклонит шаблон

    template<typename T> struct HasX { 
        struct Fallback { int x; }; // introduce member name "x"
        struct Derived : T, Fallback { };
    
        template<typename C, C> struct ChT; 
    
        template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1]; 
        template<typename C> static char (&f(...))[2]; 
    
        static bool const value = sizeof(f<Derived>(0)) == 2;
    }; 
    
    struct A { int x; };
    struct B { int X; };
    
    int main() { 
        std::cout << HasX<A>::value << std::endl; // 1
        std::cout << HasX<B>::value << std::endl; // 0
    }
    

    Это основано на блестящей идее кого-то в сети Usenet.

    Примечание. HasX проверяет любые данные или член функции с именем x произвольного типа. Единственная цель введения имени члена состоит в том, чтобы иметь возможную неоднозначность для поиска имени члена - тип члена не важен.

  • 0

    Мы можем получить во время компиляции:

    0 - not_member, 1 - is_object, 2 - is_function для каждого необходимого класса и члена - объект или функция:http://ideone.com/Fjm9u5

    #include <iostream>
    #include <type_traits>
    
    #define IS_MEMBER(T1, M)    \
    struct {        \
        struct verystrangename1 { bool M; };    \
        template<typename T> struct verystrangename2 : verystrangename1, public T { }; \
        \
        enum return_t { not_member, is_object, is_function }; \
        template<typename T, typename = decltype(verystrangename2<T>::M)> constexpr return_t what_member() { return not_member;  }  \
        template<typename T> typename std::enable_if<std::is_member_object_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_object; }   \
        template<typename T> typename std::enable_if<std::is_member_function_pointer<decltype(&T::M)>::value, return_t>::type constexpr what_member() { return is_function; }   \
        constexpr operator return_t() { return what_member<T1>(); } \
    }
    
    struct t {
        int aaa;
        float bbb;
        void func() {}
    };
    
    // Can't be in function
    IS_MEMBER(t, aaa) is_aaa_member_of_t;
    IS_MEMBER(t, ccc) is_ccc_member_of_t;
    IS_MEMBER(t, func) is_func_member_of_t;
    
    // known at compile time
    enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t };
    static constexpr int const_is_func_member_of_t = is_func_member_of_t;
    
    int main() {        
        std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" <<
            "is aaa member of t = " << is_aaa_member_of_t << std::endl << 
            "is ccc member of t = " << is_ccc_member_of_t << std::endl << 
            "is func member of t = " << is_func_member_of_t << std::endl << 
            std::endl;
    
        return 0;
    }
    

    Результат:

    0 - not_member, 1 - is_object, 2 - is_function 
    
    is aaa member of t = 1
    is ccc member of t = 0
    is func member of t = 2
    

    Для класса / структуры:

    struct t {
        int aaa;
        float bbb;
        void func() {}
    };
    

  • 2

    Почему вы не используете такую специализацию:

    struct P1 {int x; };
    struct P2 {int X; };
    
    template<class P> 
    bool Check_x(P p) { return true; }
    
    template<> 
    bool Check_x<P2>(P2 p) { return false; }
    

  • 1

    Почему бы вам просто не создать шаблонную специализацию Check_x?

    template<> bool Check_x(P1 p) { return true; }
    template<> bool Check_x(P2 p) { return false; }
    

    Черт возьми, когда я об этом думаю. Если у вас есть только два типа, зачем вам шаблоны для этого?

  • 7

    UPDATE: I've recently done some more with the code I posted in my original answer, so I'm updating this to account for changes/additions.

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

    Check for member x in a given class. Could be var, func, class, union, or enum:

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

    Check for member function 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;
    

    Check for member variable x:

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

    Check for member class x:

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

    Check for member union x:

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

    Check for member enum x:

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

    Check for any member function x regardless of signature:

    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;
    

    Details and core:

    /*
        - 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.
    */
    
    template <typename... Args> struct ambiguate : public Args... {};
    
    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;
    };
    

    Macros (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)
    

  • 4

    has_member_##name<T>

    Boost.ConceptTraits обеспечивает между другими некоторые макросы для определения черт типа, как, например,BOOST_TT_EXT_DEFINE_HAS_MEMBER(name), который определяет черту типа в форме:

    Это дает истину, если T имеет тип члена с именем. Отметим, однако, что это не обнаружит элементы ссылочного типа.

    В вашем случае будет достаточно добавить в заголовочный файл

    BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)
    

    и проверьте следующее

    BOOST_STATIC_ASSERT(has_member_x<P1>::value);
    

    Используемая техника такая же, как и объясненная в некоторых из предыдущих ответов.

    К сожалению, эта библиотека больше не поддерживается. Теперь, когда C ++ 0x не включает концепцию, эта библиотека вместе с SFINAE является идеальной заменой для работы с большинством концепций.

  • 30

    Я был перенаправлен сюда с

    вопрос который был закрыт как дубликат этого. Я знаю, что это старый поток, но я просто хотел предложить альтернативную (более простую?) Реализацию, которая работает с C ++ 11. Предположим, мы хотим проверить, есть ли у определенного класса переменная-член с именемid:

    #include <type_traits>
    
    template<typename T, typename = void>
    struct has_id : std::false_type { };
    
    template<typename T>
    struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
    

    Вот и все. А вот как это будет использоваться (live example):

    #include <iostream>
    
    using namespace std;
    
    struct X { int id; };
    struct Y { int foo; };
    
    int main()
    {
        cout << boolalpha;
        cout << has_id<X>::value << endl;
        cout << has_id<Y>::value << endl;
    }
    

    С парой макросов можно сделать еще проще:

    #define DEFINE_MEMBER_CHECKER(member) \
        template<typename T, typename V = bool> \
        struct has_ ## member : false_type { }; \
        template<typename T> \
        struct has_ ## member<T, \
            typename enable_if< \
                !is_same<decltype(declval<T>().member), void>::value, \
                bool \
                >::type \
            > : true_type { };
    
    #define HAS_MEMBER(C, member) \
        has_ ## member<C>::value
    

    Который может быть использован таким образом:

    using namespace std;
    
    struct X { int id; };
    struct Y { int foo; };
    
    DEFINE_MEMBER_CHECKER(foo)
    
    int main()
    {
        cout << boolalpha;
        cout << HAS_MEMBER(X, foo) << endl;
        cout << HAS_MEMBER(Y, foo) << endl;
    }
    

  • 2

    Второй ответ (буквально) на это показывает, как обнаружить участника:

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

  • 1

    Являются ли функции (x

    X, y, Y) из абстрактного базового класса или они могут быть реорганизованы таким образом? Если это так, вы можете использовать макрос SUPERSUBCLASS () из Modern C ++ Design, а также идеи из ответа на этот вопрос:

    Отправка на основе типа времени компиляции

  • 72

    Вот решение проще, чем

    Йоханнес Шауб - Литб& APOS; sодин, Требуется C ++ 11.

    #include <type_traits>
    
    template <typename T, typename = int>
    struct HasX : std::false_type { };
    
    template <typename T>
    struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
    

    Update: Быстрый пример и объяснение того, как это работает.

    Для этих типов:

    struct A { int x; };
    struct B { int y; };
    

    у нас естьHasX<A>::value == true а такжеHasX<B>::value == false, Давайте посмотрим, почему.

    Сначала вспомните, чтоstd::false_type а такжеstd::true_type иметьstatic constexpr bool член по имениvalue который установлен вfalse а такжеtrueсоответственно. Следовательно, два шаблонаHasX выше наследуют этот член. (Первый шаблон изstd::false_type а второй изstd::true_type.)

    Давайте начнем с простого, а затем продолжим шаг за шагом, пока не доберемся до кода выше.

    1) Начальная точка:

    template <typename T, typename U>
    struct HasX : std::false_type { };
    

    В этом случае нет ничего удивительного:HasX происходит отstd::false_type и поэтомуHasX<bool, double>::value == false а такжеHasX<bool, int>::value == false.

    2) ДефолтU:

    // Primary template
    template <typename T, typename U = int>
    struct HasX : std::false_type { };
    

    При условииU по умолчаниюint, Has<bool> на самом деле означаетHasX<bool, int> и поэтому,HasX<bool>::value == HasX<bool, int>::value == false.

    3) Добавление специализации:

    // Primary template
    template <typename T, typename U = int>
    struct HasX : std::false_type { };
    
    // Specialization for U = int
    template <typename T>
    struct HasX<T, int> : std::true_type { };
    

    В общем, благодаря первичному шаблону,HasX<T, U> происходит отstd::false_type, Тем не менее, существует специализация дляU = int который вытекает изstd::true_type, Следовательно,HasX<bool, double>::value == false ноHasX<bool, int>::value == true.

    Спасибо по умолчанию дляU, HasX<bool>::value == HasX<bool, int>::value == true.

    4) decltype и причудливый способ сказатьint:

    Небольшое отступление здесь, но, пожалуйста, потерпите меня.

    В основном (это не совсем правильно),decltype(expression) дает типexpression, Например,0 имеет типint Таким образом,decltype(0) средстваint, Аналогично,1.2 имеет типdouble и поэтому,decltype(1.2) средстваdouble.

    Рассмотрим функцию с этим объявлением:

    char func(foo, int);
    

    гдеfoo это некоторый тип класса. Еслиf является объектом типаfoo, затемdecltype(func(f, 0)) средстваchar (тип, возвращаемыйfunc(f, 0)).

    Теперь выражение(1.2, 0) использует (встроенный) оператор запятой, который вычисляет два подвыражения по порядку (то есть сначала1.2 а потом0), отбрасывает первое значение и приводит ко второму. Следовательно,

    int x = (1.2, 0);
    

    эквивалентно

    int x = 0;
    

    Положить это вместе сdecltype дает этоdecltype(1.2, 0) средстваint, В этом нет ничего особенного1.2 или жеdouble Вот. Например,true имеет типbool а такжеdecltype(true, 0) средстваint также.

    Как насчет типа класса? Для instace, что делаетdecltype(f, 0) имею в виду? Естественно ожидать, что это все еще означаетint но это может быть не так. Действительно, может быть перегрузка для оператора запятой, аналогичного функцииfunc выше этого занимаетfoo иint и возвращаетchar, В этом случае,decltype(foo, 0) являетсяchar.

    Как мы можем избежать использования перегрузки для оператора запятой? Ну, нет способа перегрузить оператор запятой дляvoid операнд, и мы можем бросить все, чтоvoid, Следовательно,decltype((void) f, 0) средстваint, В самом деле,(void) f слепкиf отfoo вvoid который в основном ничего не делает, но говорит, что выражение должно рассматриваться как имеющий типvoid, Затем используется встроенная запятая оператора и((void) f, 0) результаты в0 который имеет типint, Следовательно,decltype((void) f, 0) средстваint.

    Действительно ли этот актерский состав необходим? Хорошо, если нет перегрузки для оператора запятой, принимающегоfoo а такжеint тогда это не является необходимым. Мы всегда можем проверить исходный код, чтобы увидеть, есть ли такой оператор или нет. Однако, если это появится в шаблоне иf имеет типV который является параметром шаблона, тогда уже не ясно (или даже невозможно знать), существует ли такая перегрузка для оператора запятой или нет. Чтобы быть универсальным, мы все равно играем.

    Нижняя линия:decltype((void) f, 0) это модный способ сказатьint.

    5) СФИНАЕ:

    Это целая наука ;-) Хорошо, я преувеличиваю, но это тоже не очень просто. Таким образом, я сведу объяснение к минимуму.

    SFINAE означает «Ошибка замещения не является ошибкой». Это означает, что когда параметр шаблона заменяется типом, может появиться недопустимый код C ++, но,in some circunstancesвместо прерывания компиляции компилятор просто игнорирует нарушающий код, как если бы его там не было. Давайте посмотрим, как это применимо к нашему случаю:

    // Primary template
    template <typename T, typename U = int>
    struct HasX : std::false_type { };
    
    // Specialization for U = int
    template <typename T>
    struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };
    

    Снова здесь,decltype((void) T::x, 0) это модный способ сказатьint но с пользой СФИНА.

    когдаT заменяется типом, может появиться недопустимая конструкция. Например,bool::x не является допустимым C ++, поэтому подставляяT сbool вT::x дает неверную конструкцию. В соответствии с принципом SFINAE компилятор не отклоняет код, он просто игнорирует его (части). Точнее, как мы уже виделиHasX<bool> значит на самом делеHasX<bool, int>, Специализация дляU = int должен быть выбран, но при его создании компилятор находитbool::x и полностью игнорирует специализацию шаблона, как если бы он не существовал.

    На данный момент код по сути такой же, как и в случае (2) выше, где существует только основной шаблон. Следовательно,HasX<bool, int>::value == false.

    Тот же аргумент, используемый дляbool держит дляB посколькуB::x неверная конструкция (B не имеет членаx). Тем не мение,A::x все в порядке, и компилятор не видит проблем в создании специализации дляU = int (или, точнее, дляU = decltype((void) A::x, 0)). Следовательно,HasX<A>::value == true.

    6) Отмена имениU:

    Хорошо, посмотрев на код в (5) еще раз, мы видим, что имяU нигде не используется, но в своем объявлении (typename U). Затем мы можем отменить имя второго аргумента шаблона и получить код, показанный в верхней части этого поста.