Как проверить, есть ли у класса метод
От: ArtK  
Дата: 14.10.14 18:57
Оценка: 15 (2)
Привет.
На cppcon 2014 был доклад для про современное метопрограммирование шаблонов — слайды.
Там в частности рассказывалось как реализовать проверку существует ли для класса оператор копирующего присваивания. Суть метода в том, чтобы получить тип результата некоторого выражения (в данном случае оператора присваивания) через decltype, и инстанцировать этим типом шаблон. Если выражение корректно (то есть у нас есть оператор присваивания), то результатом параметризации будет один тип (например std::true_type), если нет, то другой тип (например std::false_type).

Пример кода из слайдов:
template< class T >
using copy_assign_t = decltype( declval<T&>( ) = declval< T const& >( ) );

template< class T >
struct is_copy_assignable {
private:
  template< class U, class = copy_assign_t<U> >
  static true_type try_assign( U&& );               // SFINAE may apply!
  static false_type try_assign( . . . );            // catch-all overload
public:
  using type = decltype( try_assign( declval<T>( ) ) );
};

Я попробовал написать общий класс, который возвращает true_type/false_type в зависимости от того, является ли любое переданное выражение корректным. Его можно использовать, чтобы реализовать свои проверки для типов, при этом не требуется писать много кода.
Например нужно проверить, есть ли в классе нестатическая функция get_value():
//Объявляем тип выражения
template <typename T>
using has_get_value_t = decltype(std::declval<T>().get_value());

template <typename T>
struct has_get_value : is_correct_expression_t<T, has_get_value_t> { };

int main() {
  std::cout << std::is_same<std::true_type, has_get_value<MyClass>::type>::value << std::endl;
}

Как это реализовано:
template <typename T, template<class> class E>
struct is_correct_expression_t {
private:
    template <typename U, typename = E<U>>
    static std::true_type try_evaluate(U&&);
    static std::false_type try_evaluate(...);
public:
    using type = decltype(try_evaluate(std::declval<T>()));
};

Для move/copy assign будет выглядеть так:
template <typename T>
using copy_assign_t = decltype(std::declval<T&>() = std::declval<const T&>());

template <typename T>
struct is_copy_assignable : is_correct_expression_t<T, copy_assign_t> { };

template <typename T>
using move_assign_t = decltype(std::declval<T&>() = std::declval<T&&>());

template <typename T>
struct is_move_assignable : is_correct_expression_t<T, move_assign_t> { };

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