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

Просто напишите

Class ofArray = o.getClass().getComponentType();

Из JavaDoc :

public Class getComponentType()

Возвращает Class, представляющий тип компонента массива. Если этот класс не представляет класс массива, этот метод возвращает null.

blockquote>

441
задан Justin 3 May 2017 в 17:35
поделиться

6 ответов

Да, с SFINAE можно проверить, предоставляет ли данный класс действительно определенный метод. Вот рабочий код:

#include <iostream>

struct Hello
{
    int helloworld() { return 0; }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two { char x[2]; };

    template <typename C> static one test( typeof(&C::helloworld) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
    std::cout << has_helloworld<Hello>::value << std::endl;
    std::cout << has_helloworld<Generic>::value << std::endl;
    return 0;
}

я только что протестировал его с Linux и gcc 4.1/4.3. Я не знаю, портативно ли это на другие платформы, запускающие различные компиляторы.

301
ответ дан L. F. 3 May 2017 в 17:35
поделиться

Теперь это было хорошо мало загадки - большой вопрос!

Вот альтернатива решение Nicola Bonelli, которое не полагается на нестандартное typeof оператор.

, К сожалению, это не работает над GCC (MinGW) 3.4.5 или Цифровым Марсом 8.42n, но это действительно работает над всеми версиями MSVC (включая VC6) и на C++ Comeau.

более длительный блок комментария имеет детали о том, как он работает (или, как предполагается, работает). Как это говорит, я не уверен, какое поведение является совместимыми стандартами - я приветствовал бы комментарий относительно этого.

<час>

обновление - 7 ноября 2008:

Это похоже, в то время как этот код синтаксически корректен, поведение, что MSVC и шоу C++ Comeau не следуют стандарту (благодаря Leon Timmermans и litb для указания на меня в правильном направлении). C++ 03 стандарта говорит следующее:

14.6.2 Зависимых абзацев 3

[temp.dep]

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

Так, похоже что, когда MSVC или Comeau рассматривают toString() функция членства T работающий поиск имени на сайте вызова в doToString(), когда шаблон инстанцируют, который является неправильным (даже при том, что это - на самом деле поведение, которое я искал в этом случае).

поведение GCC и Цифрового Марса надеется быть корректным - в обоих случаях лицо, не являющееся членом какой-либо организации, toString(), функция связывается с вызовом.

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

<час>
#include <iostream>
#include <string>

struct Hello
{
    std::string toString() {
        return "Hello";
    }
};

struct Generic {};


// the following namespace keeps the toString() method out of
//  most everything - except the other stuff in this
//  compilation unit

namespace {
    std::string toString()
    {
        return "toString not defined";
    }

    template <typename T>
    class optionalToStringImpl : public T
    {
    public:
        std::string doToString() {

            // in theory, the name lookup for this call to 
            //  toString() should find the toString() in 
            //  the base class T if one exists, but if one 
            //  doesn't exist in the base class, it'll 
            //  find the free toString() function in 
            //  the private namespace.
            //
            // This theory works for MSVC (all versions
            //  from VC6 to VC9) and Comeau C++, but
            //  does not work with MinGW 3.4.5 or 
            //  Digital Mars 8.42n
            //
            // I'm honestly not sure what the standard says 
            //  is the correct behavior here - it's sort 
            //  of like ADL (Argument Dependent Lookup - 
            //  also known as Koenig Lookup) but without
            //  arguments (except the implied "this" pointer)

            return toString();
        }
    };
}

template <typename T>
std::string optionalToString(T & obj)
{
    // ugly, hacky cast...
    optionalToStringImpl<T>* temp = reinterpret_cast<optionalToStringImpl<T>*>( &obj);

    return temp->doToString();
}



int
main(int argc, char *argv[])
{
    Hello helloObj;
    Generic genericObj;

    std::cout << optionalToString( helloObj) << std::endl;
    std::cout << optionalToString( genericObj) << std::endl;
    return 0;
}
6
ответ дан Community 3 May 2017 в 17:35
поделиться

C++ позволяет SFINAE использоваться для этого (заметьте, что с C++ 11 функций это - simplier, потому что это поддерживает расширенный SFINAE почти по произвольным выражениям - ниже, был обработан для работы с общим C++ 03 компиляторов):

#define HAS_MEM_FUNC(func, name)                                        \
    template<typename T, typename Sign>                                 \
    struct name {                                                       \
        typedef char yes[1];                                            \
        typedef char no [2];                                            \
        template <typename U, U> struct type_check;                     \
        template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
        template <typename   > static no  &chk(...);                    \
        static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     \
    }

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

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> void
doSomething() {
   if(has_to_string<T, std::string(T::*)()>::value) {
      ...
   } else {
      ...
   }
}

, Но примечание, что Вы не можете только назвать тот toString функция в этом если ответвление. так как компилятор проверит на законность в обоих ответвлениях, которые перестали бы работать для случаев, функция не существует. Один путь состоит в том, чтобы использовать SFINAE еще раз (enable_if, может быть получен от повышения также):

template<bool C, typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false, T> { };

HAS_MEM_FUNC(toString, has_to_string);

template<typename T> 
typename enable_if<has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T has toString ... */
   return t->toString();
}

template<typename T> 
typename enable_if<!has_to_string<T, 
                   std::string(T::*)()>::value, std::string>::type
doSomething(T * t) {
   /* something when T doesnt have toString ... */
   return "T::toString() does not exist.";
}

Весело проводят время с помощью него. Преимущество его состоит в том, что это также работает на перегруженные функции членства, и также на функции членства константы (не забудьте использовать std::string(T::*)() const в качестве типа указателя функции членства тогда!).

159
ответ дан Johannes Schaub - litb 3 May 2017 в 17:35
поделиться

Это - то, для чего черты типа там. К сожалению, они должны быть определены вручную. В Вашем случае вообразите следующее:

template <typename T>
struct response_trait {
    static bool const has_tostring = false;
};

template <>
struct response_trait<your_type_with_tostring> {
    static bool const has_tostring = true;
}
29
ответ дан Peter G. 3 May 2017 в 17:35
поделиться

Стандартное решение C++, представленное здесь litb, не будет работать как ожидалось, если метод, окажется, будет определен в базовом классе.

Для решения, которое обрабатывает эту ситуацию, относятся к:

На русском языке: http://www.rsdn.ru/forum/message/2759773.1.aspx

английский Перевод римлянином. Perepelitsa: http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

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

В Visual Studio, я заметил, что при работе с методом, имеющим аргументы, дополнительная пара избыточных () должна быть вставлена вокруг argments для выведения () в sizeof выражении.

6
ответ дан 4 May 2017 в 03:35
поделиться
  • 1
    @sfjedi That' s все я могу думать с информацией, которую Вы обеспечили. – L.B 18 September 2012 в 07:06

Странно, но никто не предложил следующий красивый трюк, который я однажды видел на этом самом сайте:

template <class T>
struct has_foo
{
    struct S { void foo(...); };
    struct derived : S, T {};

    template <typename V, V> struct W {};

    template <typename X>
    char (&test(W<void (X::*)(), &X::foo> *))[1];

    template <typename>
    char (&test(...))[2];

    static const bool value = sizeof(test<derived>(0)) == 1;
};

Вы должны убедиться, что T - это класс. Кажется, что двусмысленность при поиске foo - это ошибка замены. Я заставил его работать на gcc, хотя не уверен, что он стандартный.

3
ответ дан 22 November 2019 в 23:01
поделиться
Другие вопросы по тегам:

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