The example I'm providing is artificial and senseless but it is a reduction of a more complex (and more logic) code.
template<typename T>
void MyTemplate(T t)
{
wstringstream stringStream;
stringStream << t << endl;
// rest of the code
}
struct MyClass {};
void func()
{
MyTemplate(1); // OK
MyTemplate(MyClass()); // error C2679: binary '<<': no operator found which takes a right-hand operand of type 'T' (or there is no acceptable conversion)
}
MyTemplate
is generic but cannot compile with any instantiation.
Is there a way to code MyTemplate
in the following manner:
void MyTemplate(T t)
{
wstringstream stringStream;
// if/directives/magic to check if object is stream-able do
stringStream << t << endl;
// else
stringStream << "object is not stream-able" << endl;
// rest of the code
}
Yes. First we need a trait to check if a given type is streamable to a given stream:
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{};
Having this in our toolbox, we can use if constexpr in our function:
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;
}
}
And thats it. It is good practice to provide a shorthand for the traitvalue:
template<typename T, typename Stream>
inline constexpr auto can_stream_v = can_stream<T, Stream>::value;
So we have our final working example:
#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;
}