Привет.
На 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> { };
Я новенький в этой чёрной магии, так что если будут замечания или предложения по улучшению буду рад выслушать.
Спасибо!