Рассмотрим эти две функции:
template <class Type,
class = typename std::enable_if</*HAS OPERATOR <<*/>::type>
void f(std::ostream& stream, const Type& value);
template <class Type,
class... DummyTypes,
class = typename std::enable_if<sizeof...(DummyTypes) == 0>::type>
void f(std::ostream& stream, const Type& value, DummyTypes...);
Поскольку невариатическая перегрузка имеет приоритет над вариадической, я хочу проверить, имеет ли тип operator<<
с std::ostream
с помощью std::enable_if
в первой версии.
Так что я должен написать вместо /*HAS OPERATOR <<*/
?
Следующее должно работать
template <class Type,
class = decltype(std::declval<std::ostream&>() << std::declval<Type>())>
void f(std::ostream& stream, const Type& value)
{
stream << value;
}
(обратите внимание, вам не нужно использовать std::enable_if
в этом случае)
Используя конечный тип возврата (1), вы можете иметь предвкушение понятий:
template <typename Type>
auto f(std::ostream& out, Type const& t) -> decltype(out << t, void()) {
// ...
}
Из-за SFINAE эту перегрузку можно выбрать, только если тип out << t
может быть решена, и это означает, что перегрузка <<
существует, который принимает оба параметра.
Единственный недостаток в том, что это не работает, если вам нужно противоположность, это включает функцию, если эта перегрузка не существует. В этом случае enable_if
стратегия (и симметричная disable_if
) надо, насколько я знаю.
(1) спасибо Simple за помощь в синтаксисе
Проще всего проверить, когда у вас есть аргументы, то есть я бы предпочел использовать что-то вроде этого:
template <typename Type>
auto f(std::ostream& out, Type const& value)
-> typename std::enable_if<sizeof(out << value) != 0>::type {
...
}
Подобный эффект можно получить, используя std::declval()
но я не уверен в создании ссылок.