Включите шаблон в зависимости от того, существует ли определенная функция - PullRequest
0 голосов
/ 27 октября 2018

Я хотел бы разработать шаблон, который автоматически предоставляет operator<<(std::ostream&, const T&) для всех классов T, для которых существует и может вызываться T::print_to(std::ostream&), чтобы я мог определить функцию печати как функцию-член (и, в в частности, воспользуйтесь виртуальными звонками).

Методом проб и ошибок мне удалось прийти к следующему:

template<typename T, typename = decltype(std::declval<T>().print_to(std::declval<std::ostream&>()))>
std::ostream &operator<<(std::ostream &s, const T &t) {
    t.print_to(s);
    return s;
}

Кажется, это работает, но так как я все еще новичок в SFINAE и подобных хитростях, я хотел бы знать, есть ли какие-либо подводные камни или улучшения, которые можно сделать. Я поставил небольшой испытательный стенд на https://ideone.com/uLJxac.

Если возможно, я бы хотел иметь решение C ++ 14, потому что я работаю с базой кода C ++ 14. Тем не менее, если использование C ++ 17 позволяет найти лучшее решение, чем мне это тоже интересно.

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

Мне кажется, что вы правильно используете SFINAE в вашем operator<<(); Я не вижу ошибок в вашем решении.

Я предлагаю другую версию (совместимую с C ++ 11, а также с C ++ 14) только потому, что требуется меньше печатных машин

template <typename T>
auto operator<< (std::ostream & s, T const & t)
   -> decltype( t.print_to(s), s )
 {
   t.print_to(s);

   return s;
 }
0 голосов
/ 27 октября 2018

Edit:

В вашем коде нет подводных камней, извините за это. Но этот ответ позволяет вам писать код, похожий на C++20 concept:

template <class T>
auto& operator << (std::ostream &out, const printable_t<T> &t)
{
    t.print_to(out);
    return out;
}

На самом деле я написал библиотеку C++17 concept_check на основе detector и может использоваться таким образом.

Для получения дополнительной информации о поддержке concept в C++20, взгляните на эти 2: Ограничения и понятия (начиная с c ++ 20) и Ограничения и понятия (TS)

Оригинальный ответ:

std :: эксперимент :: is_detector может сделать магию для вас. Хотя его нет в стандартной библиотеке, его нетрудно реализовать, и эта ссылка дает предлагаемую реализацию.

Здесь я дам вам, как обнаружить эту функцию, вместе с моей реализацией is_detected_v.

#include <type_traits>
#include <utility>
#include <ostream>

// For support for C++17 is not complete in many compiler, I also define void_t
template <class...> using void_t = void;

namespace impl {
template <class Default, class AlwaysVoid, template <class...> class Op, class ...Args>
struct detector: private std::false_type
{
    using std::false_type::value;
    using type = Default; 
};   
template <class Default, template <class...> class Op, class ...Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...>: private std::true_type
{
    using std::true_type::value;
    using type = Op<Args...>;
}; 
} // namespace impl

struct nonsuch {};

#define CONCEPT_T constexpr const static inline bool
template <template<class...> class Op, class ...Args>
CONCEPT_T is_detected_v = impl::detector<nonsuch, void, Op, Args...>::value;

// Detect whether print_to exists.    
template <class T>
using print_to_ret_t = decltype( std::declval<T>().print_to( std::declval<std::ostream&>() ) );

template <class T>
CONCEPT_T has_func_print_to_v = is_detected_v<print_to_ret_t, T>;

template <class T, std::enable_if_t< has_func_print_to_v<T> >>
using printable_t = T;

#undef  CONCEPT_T

Вы можете попытаться добавить поддержку C++14 к этому коду. Это не будет слишком сложно. CONCEPT_T необходимо изменить на constexpr const static bool, чтобы настроить на C++14.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...