Проверка наличия члена, возможно, в базовом классе, версия VS2005 / 08 - PullRequest
3 голосов
/ 07 марта 2012

В Проверка наличия члена, возможно, в базовом классе, C ++ 11 версия , мы разработали версию C ++ 11 классической черты типа проверки элементов с SFINAE на проверить унаследованные функции-члены , которые также работают с классами C ++ 11 final, но также используют функции C ++ 11 (а именно, decltype):

template<typename T>
class has_resize_method {
    struct Yes { char unused[1]; };
    struct No { char unused[2]; };
    static_assert(sizeof(Yes) != sizeof(No));

    template<class C>
    static decltype(std::declval<C>().resize(10), Yes()) test(int);
    template<class C>
    static No test(...);
public:
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

MSVC имеет final как нестандартное расширение с именем sealed со времен VS2005, но decltype был добавлен только в VS2010. Это оставляет VS2005 и 2008, где класс, помеченный как sealed, все еще нарушает классическую черту типа, и версия C ++ 11 не может использоваться.

Итак, есть ли способ сформулировать has_resize_method так, чтобы он работал и для классов VC2005 / 08 sealed тоже?

Очевидно, что использование функций только для C ++ 11 для решения проблемы, связанной только с C ++ 11 (final), вполне допустимо, поэтому можно использовать расширения только для VS для работы с VS2005 / 08- Единственная проблема sealed классов, но если есть решение, которое работает для всех трех наборов компиляторов {C ++ 11, {VS2005, VS2008}, все остальные}, это было бы круто, но, вероятно, слишком много, чтобы просить: )

Ответы [ 2 ]

4 голосов
/ 11 марта 2012

Мне удалось найти решение, которое работает во всех основных компиляторах.К сожалению, есть проверка препроцессора для MSVC, потому что он жалуется на решение для других компиляторов.Основное отличие состоит в том, что MSVC не принимает указатели на функции внутри sizeof () и, наоборот, GCC, похоже, не принимает (&C::resize == 0) в проверке.Clang с радостью принимает оба.

#include <iostream>

class Base  {
public:
    void resize(int, int, int) { }
};

class Derived : public Base {

};

class Unrelated  { };

template<typename T>
class has_resize_method {
    struct Yes { char unused[1]; };
    struct No { char unused[2]; };

#ifdef _MSC_VER
    template <class C>
    static Yes test(char (*)[(&C::resize == 0) + 1]);
#else
    template <class C>
    static Yes test(char (*)[sizeof(&C::resize) + 1]);
#endif
    template<class C>
    static No test(...);
public:
    static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

int main()  {
    std::cout << (has_resize_method<Base>::value ? "Base has method resize" : "Base has NO method resize") << std::endl;
    std::cout << (has_resize_method<Derived>::value ? "Derived has method resize" : "Derived has NO method resize") << std::endl;
    std::cout << (has_resize_method<Unrelated>::value ? "Unrelated has method resize" : "Unrelated has NO method resize") << std::endl;
    return 0;
}

Вывод:

Base has method resize
Derived has method resize
Unrelated has NO method resize

Протестировано на GCC 4.5.3, GCC 4.3.4, Clang 3.0, Visual C ++ 2008 и Visual C ++ 2010. Я надеваюУ меня нет доступа к Visual C ++ 2005, но я думаю, что он тоже будет работать.Он также компилируется на Comeau Online , но я не могу гарантировать, что он выдаст правильный вывод там.

Работает как с финальными, так и с __sealed классами.

Обратите внимание, что он проверяет не толькодля функций-членов, но для указателей на членов в целом.Возможно, вы захотите добавить дополнительные проверки, такие как boost::is_member_function_pointer, если это поведение нежелательно.Точно так же вы можете захотеть добавить проверки количества аргументов / типов аргументов / типов результатов - опять же, повышение будет очень полезно здесь, особенно. разложение типа буста .

2 голосов
/ 10 марта 2012

MSVC имеет специальный оператор __ if_exists с версии 2005 года. MSDN Ссылка здесь .Вы можете использовать его, чтобы проверить имя функции-члена напрямую.А потом проверь подпись.Ниже приведен простой пример обнаружения foo:

template <typename T, typename U>
int8_t FooCheck( void(T::*)(U) )
{
    return 0;
}

template <typename T>
int16_t FooCheck( void(T::*)(double))
{
    return 0;
}

template <typename T>
int32_t FooCheck(void(T::*)(int))
{
    return 0;
}


template <typename T>
class Detector
{
public:

    __if_exists(T::foo)
    {    
       enum
       {
           value = sizeof(FooCheck(&T::foo))
       };
    }
    __if_not_exists(T::foo)
    {
       enum
       {
           value = 0
       };
   }

};


std::cout << Detector<Class>::value << std::endl;
...