Проверка наличия члена, возможно, в базовом классе, версия C ++ 11 - PullRequest
29 голосов
/ 02 марта 2012

В https://stackoverflow.com/a/1967183/134841, предусмотрено решение для статической проверки существования члена, возможно, в подклассе типа:

template <typename Type> 
class has_resize_method
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void resize(int){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

Однако он не работает на классах C ++ 11 final, поскольку наследуется от тестируемого класса, который final предотвращает.

ОТО, вот этот:

template <typename C>
struct has_reserve_method {
private:
    struct No {};
    struct Yes { No no[2]; };
    template <typename T, typename I, void(T::*)(I) > struct sfinae {};
    template <typename T> static No  check( ... );
    template <typename T> static Yes check( sfinae<T,int,   &T::reserve> * );
    template <typename T> static Yes check( sfinae<T,size_t,&T::reserve> * );
public:
    static const bool value = sizeof( check<C>(0) ) == sizeof( Yes ) ;
};

не удается найти метод reserve(int/size_t) в базовых классах.

Существует ли реализация этой метафункции, которая находит reserved() в базовых классах T и все еще работает, если T равен final?

Ответы [ 2 ]

51 голосов
/ 02 марта 2012

На самом деле, в C ++ 11 все стало намного проще благодаря decltype и механизмам поздних привязок.

Теперь для проверки этого проще использовать методы:

// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
  return true;
}

// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }

Затем вы можете использовать это в классе, например:

template <typename T, bool b>
struct Reserver {
  static void apply(T& t, size_t n) { t.reserve(n); }
};

template <typename T>
struct Reserver <T, false> {
  static void apply(T& t, size_t n) {}
};

И вы используете это так:

template <typename T>
bool reserve(T& t, size_t n) {
  Reserver<T, has_reserve_method(t)>::apply(t, n);
  return has_reserve_method(t);
}

Или вы можете выбрать enable_if метод:

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
  t.reserve(n);
  return true;
}

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
  return false;
}

Обратите внимание, что это переключение вещей на самом деле не так просто. В целом, намного проще, когда существует только SFINAE - и вы просто хотите enable_if один метод и не предоставлять какой-либо запасной вариант:

template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
  t.reserve(n);
}

Если замена не удалась, этот метод удаляется из списка возможных перегрузок.

Примечание: благодаря семантике , (оператор запятой) вы можете объединять несколько выражений в decltype, и только последнее фактически определяет тип. Удобно для проверки нескольких операций.

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

Версия, которая также опирается на decltype, но не на передачу произвольных типов в (...) [что на самом деле не является проблемой, см. Комментарий Йоханнеса]:

template<typename> struct Void { typedef void type; };

template<typename T, typename Sfinae = void>
struct has_reserve: std::false_type {};

template<typename T>
struct has_reserve<
    T
    , typename Void<
        decltype( std::declval<T&>().reserve(0) )
    >::type
>: std::true_type {};

I 'Я хотел бы указать в соответствии с этой чертой тип, такой как std::vector<int>&, поддерживает reserve: здесь проверяются выражения, а не типы.Вопрос, на который отвечает эта черта: «Если для такого типа T задано lvalue *1009*, это правильные выражения lval.reserve(0);».Не совпадает с вопросом «Имеет ли этот тип или какой-либо из его базовых типов объявленный reserve член».

С другой стороны, возможно, это особенность!Помните, что новая черта C ++ 11 имеет стиль is_default_constructible, , а не has_default_constructor.Различие тонкое, но имеет свои достоинства.(Поиск более подходящего имени в стиле is_*ible оставлено в качестве упражнения.)

В любом случае вы все равно можете использовать такую ​​черту, как std::is_class, чтобы достичь желаемого.

...