Итак, перед нами конфликт первой и второй функции, и надо первую как-то ограничить.
Вариант 1. Концепция Си++20.
template <class T>
concept Printable = requires(T x) {
std::cout << x;
};
struct Class {
template<Printable Text>
Class& operator<<(const Text& text) {
cout << text << endl;
return *this;
}
Вариант 2. Обратная концепция.
template<uint8_t i>
struct Id {
constexpr static uint8_t id = i;
using SpecialPrint = void;
// какие-то элементы класса с методами
};
. . . . .
template <class T>
concept SpecialPrintable = requires {
typename T::SpecialPrint;
};
struct Class {
template<class Text>
Class& operator<<(const Text& text) {
cout << text << endl;
return *this;
}
template <SpecialPrintable Special>
Class& operator<<(const Special& text) {
specialPrint(text);
return *this;
}
template<uint8_t i>
void specialPrint(const Id<i>& text) {
cout << (int)i << endl;
}
};
А на 17 без концепций…
template<uint8_t i>
struct Id {
constexpr static uint8_t id = i;
using SpecialPrint = void;
// какие-то элементы класса с методами
};
. . . . .
template<class T, class Dummy = void>
struct IsSpecPrintable { static constexpr bool value = false; };
template<class T>
struct IsSpecPrintable<T, typename T::SpecialPrint> { static constexpr bool value = true; };
struct Class {
template <class T>
Class& operator<<(const T& text)
{
if constexpr (IsSpecPrintable<T>::value) {
specialPrint(text);
} else {
normalPrint(text);
}
return *this;
}
template<class Text>
void normalPrint(const Text& text) {
cout << text << endl;
}
template<uint8_t i>
void specialPrint(const Id<i>& text) {
cout << (int)i << endl;
}
};