Вы можете использовать is_detected для этого. Мы пытаемся проверить, можно ли печатать данный тип с одной из перегрузок std::cout
s, равной operator<<
, или сам тип предоставляет operator<<
для печати на std::cout
. Для более общего объяснения того, как я это реализовал, посмотрите https://www.fluentcpp.com/2017/06/02/write-template-metaprogramming-expressively/
Сначала мы определим соответствующий is_detected
для перегрузок самого std :: cout:
// check std::cout.operator<<(T {})
template<typename = void, typename Arg = void> struct test_operator_of_cout : std::false_type {};
template<typename Arg>
struct test_operator_of_cout<std::void_t<decltype(std::cout.operator<<(std::declval<Arg>()))>, Arg>
: std::true_type {};
template<typename Arg>
constexpr bool test_operator_of_cout_v = test_operator_of_cout<void, Arg>::value;
И еще один для всех перегрузок operator<<(ostream&, T {})
. Ссылка, которую я разместил выше, обобщает это, чтобы иметь меньшую избыточность кода.
// check operator<<(std::cout, T {})
template<typename = void, typename Arg = void> struct test_operator_of_struct : std::false_type {};
template<typename Arg>
struct test_operator_of_struct<std::void_t<decltype(operator<<(std::cout, std::declval<Arg>()))>, Arg>
: std::true_type {};
template<typename Arg>
constexpr bool test_operator_of_struct_v = test_operator_of_struct<void, Arg>::value;
Теперь мы можем использовать эти черты типа для реализации функции печати с помощью enable_if:
template<typename T> struct MyClass {
T t;
template<
typename Consider = T,
typename = std::enable_if_t<
( test_operator_of_cout_v<Consider> || test_operator_of_struct_v<Consider>)
&& !std::is_arithmetic_v<Consider>
>
> void print() {
std::cout << t;
}
};
Есть две вещи отметить здесь:
- Вам нужен первый аргумент шаблона
Consider = T
. В противном случае компилятор попытается создать экземпляр объявления функции, который не подходит для типов, которые не выполняют условие. Посмотрите этот SO-ответ для более подробного объяснения: std :: enable_if для условной компиляции функции-члена - Арифметические типы c не печатаются из-за
!std::is_arithmetic
, Я лично не включил бы это, потому что я не вижу причины, по которой мой класс не должен позволять печатать идеально печатные типы.
Теперь мы можем посмотреть, что можно печатать, а что нет:
struct NotPrintable {};
struct Printable {
friend std::ostream& operator<<(std::ostream& os, const Printable& p) {
return os;
}
};
auto foo() {
MyClass<const char *> chars;
chars.print(); //compiles
MyClass<std::string> strings;
strings.print(); //compiles
MyClass<std::string_view> string_views;
string_views.print(); //compiles
MyClass<Printable> printables;
printables.print(); // compiles
// MyClass<int> ints;
// ints.print(); // Does not compile due to !is_arithmetiv_v
// MyClass<NotPrintable> not_printable;
// not_printable.print(); //Does not compile due to operator checking
}
Вы можете посмотреть полный пример здесь: https://godbolt.org/z/ZC9__e