Да. Сначала нам нужна черта, чтобы проверить, является ли данный тип пригодным для потока:
template<typename T, typename Stream, typename = void>
struct can_stream : std::false_type{};
template<typename T, typename Stream>
struct can_stream<T, Stream, std::void_t<decltype(std::declval<Stream>() << std::declval<T>())>> : std::true_type{};
Имея это в нашем наборе инструментов, мы можем использовать if constexpr в нашей функции:
template<typename T>
void MyTemplate(T t)
{
wstringstream stringStream;
if constexpr(can_stream<T, std::wstringstream>::value)
{
stringStream << t << endl;
}
else
{
stringStream << "object is not stream-able" << endl;
}
}
Вот и все. Хорошей практикой является сокращение сокращенного значения:
template<typename T, typename Stream>
inline constexpr auto can_stream_v = can_stream<T, Stream>::value;
Итак, у нас есть последний рабочий пример:
#include <iostream>
#include <sstream>
#include <type_traits>
using namespace std;
template<typename T, typename Stream, typename = void>
struct can_stream : std::false_type{};
template<typename T, typename Stream>
struct can_stream<T, Stream, std::void_t<decltype(std::declval<Stream>() << std::declval<T>())>> : std::true_type{};
template<typename T, typename Stream>
inline constexpr auto can_stream_v = can_stream<T, Stream>::value;
template<typename T>
void MyTemplate(T t)
{
wstringstream stringStream;
if constexpr(can_stream_v<T, std::wstringstream>)
{
stringStream << t << endl;
}
else
{
stringStream << "object is not stream-able" << endl;
}
std::wcout << stringStream.str();
}
struct MyClass {};
int main()
{
MyTemplate(1);
MyTemplate(MyClass());
return 0;
}